17 use Psr\Log\LoggerAwareInterface;
18 use Psr\Log\LoggerAwareTrait;
19 use Symfony\Component\HttpFoundation\Cookie;
375 if (empty($this->loginType)) {
376 throw new Exception(
'No loginType defined, should be set explicitly by subclass', 1476045345);
378 $this->logger->debug(
'## Beginning of auth logging.');
381 $this->newSessionID =
false;
384 $this->svConfig =
$GLOBALS[
'TYPO3_CONF_VARS'][
'SVCONF'][
'auth'] ?? [];
387 if (!
$id && $this->getFallBack && $this->get_name) {
388 $id = isset($_GET[$this->get_name]) ? GeneralUtility::_GET($this->get_name) :
'';
389 if (strlen(
$id) != $this->hash_length) {
400 $this->newSessionID =
true;
405 if ($mode ===
'get' && $this->getFallBack && $this->get_name) {
406 $this->get_URL_ID =
'&' . $this->get_name .
'=' .
$id;
416 if (!$this->dontSetCookie) {
420 foreach (
$GLOBALS[
'TYPO3_CONF_VARS'][
'SC_OPTIONS'][
't3lib/class.t3lib_userauth.php'][
'postUserLookUp'] ?? [] as $funcName) {
424 GeneralUtility::callUserFunction($funcName, $_params, $this);
427 if ($this->gc_time === 0) {
432 if (rand() % 100 <= $this->gc_probability) {
450 foreach ($httpHeaders as $httpHeaderName => $value) {
451 header($httpHeaderName .
': ' . $value);
465 'Last-Modified' => gmdate(
'D, d M Y H:i:s') .
' GMT'
467 $cacheControlHeader =
'no-cache, must-revalidate';
468 $pragmaHeader =
'no-cache';
471 if (strpos(GeneralUtility::getIndpEnv(
'HTTP_USER_AGENT'),
'MSIE') !==
false
472 && GeneralUtility::getIndpEnv(
'TYPO3_SSL')) {
475 $cacheControlHeader =
'must-revalidate';
477 $pragmaHeader =
'private';
479 $headers[
'Cache-Control'] = $cacheControlHeader;
480 $headers[
'Pragma'] = $pragmaHeader;
493 if ($isSetSessionCookie || $isRefreshTimeBasedCookie) {
494 $settings =
$GLOBALS[
'TYPO3_CONF_VARS'][
'SYS'];
498 $cookiePath = $cookieDomain ?
'/' : GeneralUtility::getIndpEnv(
'TYPO3_SITE_PATH');
500 $cookieExpire = $isRefreshTimeBasedCookie ?
$GLOBALS[
'EXEC_TIME'] + $this->lifetime : 0;
502 $cookieSecure = (bool)$settings[
'cookieSecure'] && GeneralUtility::getIndpEnv(
'TYPO3_SSL');
504 $cookieSameSite = $this->sanitizeSameSiteCookieValue(
505 strtolower(
$GLOBALS[
'TYPO3_CONF_VARS'][$this->loginType][
'cookieSameSite'] ?? Cookie::SAMESITE_STRICT)
508 if ($cookieSameSite === Cookie::SAMESITE_NONE) {
509 $cookieSecure =
true;
512 if ((
int)$settings[
'cookieSecure'] !== 1 || GeneralUtility::getIndpEnv(
'TYPO3_SSL')) {
513 $cookie =
new Cookie(
524 header(
'Set-Cookie: ' . $cookie->__toString(),
false);
525 $this->cookieWasSetOnCurrentRequest =
true;
527 throw new Exception(
'Cookie was not set since HTTPS was forced in $TYPO3_CONF_VARS[SYS][cookieSecure].', 1254325546);
529 $this->logger->debug(
530 ($isRefreshTimeBasedCookie ?
'Updated Cookie: ' :
'Set Cookie: ')
531 . sha1($this->
id) . ($cookieDomain ?
', ' . $cookieDomain :
'')
545 $cookieDomain =
$GLOBALS[
'TYPO3_CONF_VARS'][
'SYS'][
'cookieDomain'];
548 if (!empty(
$GLOBALS[
'TYPO3_CONF_VARS'][$this->loginType][
'cookieDomain'])) {
552 if ($cookieDomain[0] ===
'/') {
554 $matchCnt = @preg_match($cookieDomain, GeneralUtility::getIndpEnv(
'TYPO3_HOST_ONLY'), $match);
555 if ($matchCnt ===
false) {
556 $this->logger->critical(
'The regular expression for the cookie domain (' . $cookieDomain .
') contains errors. The session is not shared across sub-domains.');
557 } elseif ($matchCnt) {
561 $result = $cookieDomain;
575 return isset($_COOKIE[$cookieName]) ? stripslashes($_COOKIE[$cookieName]) :
'';
586 return ($this->newSessionID || $this->forceSetCookie) && $this->lifetime == 0;
597 return $this->lifetime > 0;
612 $authenticated =
false;
614 $activeLogin =
false;
616 $this->loginFailure =
false;
617 $this->logger->debug(
'Login type: ' . $this->loginType);
625 if ($this->writeStdLog) {
627 $this->
writelog(255, 2, 0, 2,
'User %s logged out', [$this->user[
'username']],
'', 0, 0);
629 $this->logger->info(
'User logged out. Id: ' . sha1($this->
id));
634 $skipSessionUpdate = (bool)GeneralUtility::_GP(
'skipSessionUpdate');
635 $haveSession =
false;
636 $anonymousSession =
false;
637 if (!$this->newSessionID) {
640 $haveSession = is_array($authInfo[
'userSession']);
641 if ($haveSession && !empty($authInfo[
'userSession'][
'ses_anonymous'])) {
642 $anonymousSession =
true;
649 $this->logger->debug(
'Active login (eg. with login form)');
651 if ($this->formfield_status && $loginData[
'uident'] && $loginData[
'uname']) {
658 throw new \RuntimeException(
'TYPO3 Fatal Error: You have tried to login using a CLI user. Access prohibited!', 1270853931);
668 $this->logger->debug(
'User session found', [
669 $this->userid_column => $authInfo[
'userSession'][$this->userid_column] ??
null,
670 $this->username_column => $authInfo[
'userSession'][$this->username_column] ??
null,
673 $this->logger->debug(
'No user session found');
675 if (is_array($this->svConfig[
'setup'] ??
false)) {
676 $this->logger->debug(
'SV setup', $this->svConfig[
'setup']);
681 $activeLogin || !empty($this->svConfig[
'setup'][$this->loginType .
'_alwaysFetchUser'])
682 || !$haveSession && !empty($this->svConfig[
'setup'][$this->loginType .
'_fetchUserIfNoSession'])
687 foreach ($this->
getAuthServices($subType, $loginData, $authInfo) as $serviceObj) {
688 if ($row = $serviceObj->getUser()) {
689 $tempuserArr[] = $row;
690 $this->logger->debug(
'User found', [
691 $this->userid_column => $row[$this->userid_column],
692 $this->username_column => $row[$this->username_column],
695 if (empty($this->svConfig[
'setup'][$this->loginType .
'_fetchAllUsers'])) {
701 if (!empty($this->svConfig[
'setup'][$this->loginType .
'_alwaysFetchUser'])) {
702 $this->logger->debug($this->loginType .
'_alwaysFetchUser option is enabled');
704 if (empty($tempuserArr)) {
705 $this->logger->debug(
'No user found by services');
707 $this->logger->debug(count($tempuserArr) .
' user records found by services');
712 if (empty($tempuserArr) && $haveSession && !$anonymousSession) {
713 $tempuserArr[] = $authInfo[
'userSession'];
714 $tempuser = $authInfo[
'userSession'];
716 $authenticated =
true;
717 $this->logger->debug(
'User session used', [
718 $this->userid_column => $authInfo[
'userSession'][$this->userid_column],
719 $this->username_column => $authInfo[
'userSession'][$this->username_column],
723 if (!empty($this->svConfig[
'setup'][$this->loginType .
'_alwaysAuthUser'])) {
724 $authenticated =
false;
725 $this->logger->debug(
'alwaysAuthUser option is enabled');
728 if (!empty($tempuserArr) && !$authenticated) {
729 foreach ($tempuserArr as $tempuser) {
736 foreach ($this->
getAuthServices($subType, $loginData, $authInfo) as $serviceObj) {
737 if (($ret = $serviceObj->authUser($tempuser)) > 0) {
739 if ((
int)$ret >= 200) {
740 $authenticated =
true;
743 if ((
int)$ret >= 100) {
745 $authenticated =
true;
748 $authenticated =
false;
753 if ($authenticated) {
761 if ($authenticated) {
763 $this->loginFailure =
false;
765 if (!$haveSession || $anonymousSession || $tempuser[
'ses_id'] != $this->
id && $tempuser[
'uid'] != $authInfo[
'userSession'][
'ses_userid']) {
769 if ($anonymousSession) {
772 [
'ses_data' => $authInfo[
'userSession'][
'ses_data']]
776 $this->user = array_merge(
781 $this->loginSessionStarted =
true;
782 if (is_array($this->user)) {
783 $this->logger->debug(
'User session finally read', [
784 $this->userid_column => $this->user[$this->userid_column],
785 $this->username_column => $this->user[$this->username_column],
788 } elseif ($haveSession) {
791 if ($this->
getSessionBackend() instanceof HashableSessionBackendInterface && !empty($authInfo[
'userSession'][
'ses_id'] ??
'')) {
793 if ($authInfo[
'userSession'][
'ses_id'] === $this->
id) {
796 [
'ses_data' => $authInfo[
'userSession'][
'ses_data']]
801 $this->user = $authInfo[
'userSession'];
804 if ($activeLogin && !$this->newSessionID) {
809 if ($this->writeStdLog && $activeLogin) {
810 $this->
writelog(255, 1, 0, 1,
'User %s logged in from ###IP###', [$tempuser[$this->username_column]],
'',
'',
'');
813 $this->logger->info(
'User ' . $tempuser[$this->username_column] .
' logged in from ' . GeneralUtility::getIndpEnv(
'REMOTE_ADDR'));
816 $this->logger->debug(
'User ' . $tempuser[$this->username_column] .
' authenticated from ' . GeneralUtility::getIndpEnv(
'REMOTE_ADDR'));
820 if ($anonymousSession) {
821 $this->user = $authInfo[
'userSession'];
825 if ($activeLogin || !empty($tempuserArr)) {
826 $this->loginFailure =
true;
827 if (empty($tempuserArr) && $activeLogin) {
831 $this->logger->debug(
'Login failed', $logData);
833 if (!empty($tempuserArr)) {
838 $this->logger->debug(
'Login failed', $logData);
844 if ($this->loginFailure && $activeLogin) {
845 $this->logger->debug(
846 'Call checkLogFailures',
848 'warningEmail' => $this->warningEmail,
849 'warningPeriod' => $this->warningPeriod,
850 'warningMax' => $this->warningMax
857 foreach (
$GLOBALS[
'TYPO3_CONF_VARS'][
'SC_OPTIONS'][
't3lib/class.t3lib_userauth.php'][
'postLoginFailureProcessing'] ?? [] as $_funcRef) {
858 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
867 $this->
checkLogFailures($this->warningEmail, $this->warningPeriod, $this->warningMax);
878 return GeneralUtility::makeInstance(Random::class)->generateRandomHexString($this->hash_length);
889 protected function getAuthServices(
string $subType, array $loginData, array $authInfo): \Traversable
892 while (is_object($serviceObj = GeneralUtility::makeInstanceService(
'auth', $subType, $serviceChain))) {
893 $serviceChain .=
',' . $serviceObj->getServiceKey();
894 $serviceObj->initAuth($subType, $loginData, $authInfo, $this);
898 $this->logger->debug($subType .
' auth services called: ' . $serviceChain);
910 protected function regenerateSessionId(array $existingSessionRecord = [],
bool $anonymous =
false)
912 if (empty($existingSessionRecord)) {
920 $this->sessionData = unserialize($updatedSession[
'ses_data']);
922 $this->user = array_merge($this->user ?? [], $updatedSession);
924 $this->newSessionID =
true;
942 $this->logger->debug(
'Create session ses_id = ' . sha1($this->
id));
950 return $sessionRecord;
960 if ($this->lastLogin_column) {
961 $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->user_table);
964 [$this->lastLogin_column =>
$GLOBALS[
'EXEC_TIME']],
965 [$this->userid_column => $userId]
979 $sessionIpLock =
'[DISABLED]';
980 if ($this->lockIP && empty($tempuser[
'disableIPlock'])) {
986 'ses_iplock' => $sessionIpLock,
1001 $this->logger->debug(
'Fetch session ses_id = ' . sha1($this->
id));
1004 }
catch (SessionNotFoundException $e) {
1008 $this->sessionData = unserialize($sessionRecord[
'ses_data']);
1010 if (!empty($sessionRecord[
'ses_anonymous'])) {
1011 return $sessionRecord;
1017 $userRecord = array_merge($sessionRecord, $userRecord);
1019 $userRecord[
'ses_tstamp'] = (int)$userRecord[
'ses_tstamp'];
1020 $userRecord[
'is_online'] = (int)$userRecord[
'ses_tstamp'];
1022 if (!empty($this->auth_timeout_field)) {
1024 $timeout = (int)$userRecord[$this->auth_timeout_field];
1030 if ($timeout > 0 &&
$GLOBALS[
'EXEC_TIME'] < $userRecord[
'ses_tstamp'] + $timeout) {
1031 $sessionUpdateGracePeriod = 61;
1032 if (!$skipSessionUpdate &&
$GLOBALS[
'EXEC_TIME'] > ($userRecord[
'ses_tstamp'] + $sessionUpdateGracePeriod)) {
1035 $userRecord = array_merge($userRecord, $updatesSession);
1040 $userRecord =
false;
1064 $this->logger->debug(
'logoff: ses_id = ' . sha1($this->
id));
1067 foreach (
$GLOBALS[
'TYPO3_CONF_VARS'][
'SC_OPTIONS'][
't3lib/class.t3lib_userauth.php'][
'logoff_pre_processing'] ?? [] as $_funcRef) {
1069 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1074 foreach (
$GLOBALS[
'TYPO3_CONF_VARS'][
'SC_OPTIONS'][
't3lib/class.t3lib_userauth.php'][
'logoff_post_processing'] ?? [] as $_funcRef) {
1076 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1091 $this->sessionData = [];
1104 $cookiePath = $cookieDomain ?
'/' : GeneralUtility::getIndpEnv(
'TYPO3_SITE_PATH');
1105 setcookie($cookieName,
null, -1, $cookiePath, $cookieDomain);
1119 if (empty($sessionRecord)) {
1124 if ($sessionRecord[
'ses_iplock'] !== $this->
ipLockClause_remoteIPNumber($this->lockIP) && $sessionRecord[
'ses_iplock'] !==
'[DISABLED]') {
1128 }
catch (SessionNotFoundException $e) {
1141 return $this->cookieWasSetOnCurrentRequest || $this->
getCookie($this->name);
1159 $restrictionContainer = GeneralUtility::makeInstance(DefaultRestrictionContainer::class);
1161 if (empty($this->enablecolumns[
'disabled'])) {
1162 $restrictionContainer->removeByType(HiddenRestriction::class);
1165 if (empty($this->enablecolumns[
'deleted'])) {
1166 $restrictionContainer->removeByType(DeletedRestriction::class);
1169 if (empty($this->enablecolumns[
'starttime'])) {
1170 $restrictionContainer->removeByType(StartTimeRestriction::class);
1173 if (empty($this->enablecolumns[
'endtime'])) {
1174 $restrictionContainer->removeByType(EndTimeRestriction::class);
1177 if (!empty($this->enablecolumns[
'rootLevel'])) {
1178 $restrictionContainer->add(GeneralUtility::makeInstance(RootLevelRestriction::class, [$this->user_table]));
1181 return $restrictionContainer;
1193 $IP = GeneralUtility::getIndpEnv(
'REMOTE_ADDR');
1198 $IPparts = explode(
'.', $IP);
1199 for ($a = 4; $a > $parts; $a--) {
1200 unset($IPparts[$a - 1]);
1202 return implode(
'.', $IPparts);
1217 public function writeUC($variable =
'')
1219 if (is_array($this->user) && $this->user[$this->userid_column]) {
1220 if (!is_array($variable)) {
1223 $this->logger->debug(
'writeUC: ' . $this->userid_column .
'=' . (
int)$this->user[$this->userid_column]);
1224 GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->user_table)->update(
1226 [
'uc' => serialize($variable)],
1227 [$this->userid_column => (
int)$this->user[$this->userid_column]],
1241 if (!$theUC && isset($this->user[
'uc'])) {
1242 $theUC = unserialize($this->user[
'uc'], [
'allowed_classes' =>
false]);
1244 if (is_array($theUC)) {
1260 $sessionHash = GeneralUtility::hmac(
1264 $this->uc[
'moduleData'][$module] = $data;
1265 $this->uc[
'moduleSessionID'][$module] = $sessionHash;
1280 $sessionHash = GeneralUtility::hmac(
1285 $moduleSessionIdHash = $this->uc[
'moduleSessionID'][$module] ??
null;
1287 ||
$sessionData !==
null && $moduleSessionIdHash === $sessionHash
1289 ||
$sessionData !==
null && $moduleSessionIdHash === $this->
id
1305 return $this->sessionData[$key] ??
null;
1318 throw new \InvalidArgumentException(
'Argument key must not be empty', 1484311516);
1320 $this->sessionData[$key] = $data;
1332 $this->sessionData[$key] = $data;
1333 $this->user[
'ses_data'] = serialize($this->sessionData);
1334 $this->logger->debug(
'setAndSaveSessionData: ses_id = ' . sha1($this->
id));
1337 [
'ses_data' => $this->user[
'ses_data']]
1339 $this->user = array_merge($this->user ?? [], $updatedSession);
1356 $loginData[
'status'] = GeneralUtility::_GP($this->formfield_status);
1357 if ($this->getMethodEnabled) {
1358 $loginData[
'uname'] = GeneralUtility::_GP($this->formfield_uname);
1359 $loginData[
'uident'] = GeneralUtility::_GP($this->formfield_uident);
1361 $loginData[
'uname'] = GeneralUtility::_POST($this->formfield_uname);
1362 $loginData[
'uident'] = GeneralUtility::_POST($this->formfield_uident);
1368 $loginData = array_map(
'trim', $loginData);
1381 public function processLoginData($loginData, $passwordTransmissionStrategy =
'')
1383 $loginSecurityLevel = trim(
$GLOBALS[
'TYPO3_CONF_VARS'][$this->loginType][
'loginSecurityLevel']) ?:
'normal';
1384 $passwordTransmissionStrategy = $passwordTransmissionStrategy ?: $loginSecurityLevel;
1385 $this->logger->debug(
'Login data before processing', $loginData);
1389 $isLoginDataProcessed =
false;
1390 $processedLoginData = $loginData;
1391 while (is_object($serviceObject = GeneralUtility::makeInstanceService(
'auth', $subType, $serviceChain))) {
1392 $serviceChain .=
',' . $serviceObject->getServiceKey();
1393 $serviceObject->initAuth($subType, $loginData, $authInfo, $this);
1394 $serviceResult = $serviceObject->processLoginData($processedLoginData, $passwordTransmissionStrategy);
1395 if (!empty($serviceResult)) {
1396 $isLoginDataProcessed =
true;
1398 if ((
int)$serviceResult >= 200) {
1399 unset($serviceObject);
1403 unset($serviceObject);
1405 if ($isLoginDataProcessed) {
1406 $loginData = $processedLoginData;
1424 if ($isUserRecord && is_array($data)) {
1425 $fieldNames = [
'uid',
'pid',
'tstamp',
'crdate',
'cruser_id',
'deleted',
'disabled',
'starttime',
'endtime',
'username',
'admin',
'usergroup',
'db_mountpoints',
'file_mountpoints',
'file_permissions',
'workspace_perms',
'lastlogin',
'workspace_id',
'category_perms'];
1426 $data = array_intersect_key($data, array_combine($fieldNames, $fieldNames));
1428 if (isset($data[
'uident'])) {
1429 $data[
'uident'] =
'********';
1431 if (isset($data[
'uident_text'])) {
1432 $data[
'uident_text'] =
'********';
1434 if (isset($data[
'password'])) {
1435 $data[
'password'] =
'********';
1448 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->user_table);
1449 $expressionBuilder = $queryBuilder->expr();
1452 $authInfo[
'refInfo'] = parse_url(GeneralUtility::getIndpEnv(
'HTTP_REFERER'));
1453 $authInfo[
'HTTP_HOST'] = GeneralUtility::getIndpEnv(
'HTTP_HOST');
1454 $authInfo[
'REMOTE_ADDR'] = GeneralUtility::getIndpEnv(
'REMOTE_ADDR');
1455 $authInfo[
'REMOTE_HOST'] = GeneralUtility::getIndpEnv(
'REMOTE_HOST');
1464 [$this->user_table => $this->user_table],
1467 if ($this->checkPid && $this->checkPid_value !==
null) {
1469 $authInfo[
'db_user'][
'check_pid_clause'] = $expressionBuilder->in(
1471 GeneralUtility::intExplode(
',', $this->checkPid_value)
1474 $authInfo[
'db_user'][
'checkPidList'] =
'';
1475 $authInfo[
'db_user'][
'check_pid_clause'] =
'';
1492 trigger_error(
'This method will be removed in TYPO3 v10.0.', E_USER_DEPRECATED);
1519 public function writelog($type, $action, $error, $details_nr, $details, $data, $tablename, $recuid, $recpid)
1572 $query = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->user_table);
1575 ->from($this->user_table)
1576 ->where($query->expr()->eq(
'uid', $query->createNamedParameter($uid, \PDO::PARAM_INT)));
1578 return $query->execute()->fetch();
1591 $query = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->user_table);
1594 ->from($this->user_table)
1595 ->where($query->expr()->eq(
'username', $query->createNamedParameter(
$name, \PDO::PARAM_STR)));
1597 return $query->execute()->fetch();
1612 trigger_error(
'This method will be removed in TYPO3 v10.0.', E_USER_DEPRECATED);
1614 if ($username || $extraWhere) {
1615 $query = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($dbUser[
'table']);
1616 $query->getRestrictions()->removeAll()
1617 ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1619 $constraints = array_filter([
1625 if (!empty($username)) {
1629 $dbUser[
'username_column'],
1630 $query->createNamedParameter($username, \PDO::PARAM_STR)
1635 $user = $query->select(
'*')
1636 ->from($dbUser[
'table'])
1637 ->where(...$constraints)
1669 if (!isset($this->sessionBackend)) {
1670 $this->sessionBackend = GeneralUtility::makeInstance(SessionManager::class)->getSessionBackend($this->loginType);