Passed
Push — master ( 25a926...39145a )
by
unknown
20:30
created

AbstractUserAuthentication::getSessionData()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace TYPO3\CMS\Core\Authentication;
17
18
use Psr\Http\Message\ResponseInterface;
19
use Psr\Log\LoggerAwareInterface;
20
use Psr\Log\LoggerAwareTrait;
21
use Symfony\Component\HttpFoundation\Cookie;
22
use TYPO3\CMS\Core\Authentication\Mfa\MfaProviderRegistry;
23
use TYPO3\CMS\Core\Authentication\Mfa\MfaRequiredException;
24
use TYPO3\CMS\Core\Core\Environment;
25
use TYPO3\CMS\Core\Crypto\Random;
26
use TYPO3\CMS\Core\Database\Connection;
27
use TYPO3\CMS\Core\Database\ConnectionPool;
28
use TYPO3\CMS\Core\Database\Query\Restriction\DefaultRestrictionContainer;
29
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
30
use TYPO3\CMS\Core\Database\Query\Restriction\EndTimeRestriction;
31
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
32
use TYPO3\CMS\Core\Database\Query\Restriction\QueryRestrictionContainerInterface;
33
use TYPO3\CMS\Core\Database\Query\Restriction\RootLevelRestriction;
34
use TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction;
35
use TYPO3\CMS\Core\Exception;
36
use TYPO3\CMS\Core\Http\CookieHeaderTrait;
37
use TYPO3\CMS\Core\Session\Backend\Exception\SessionNotFoundException;
38
use TYPO3\CMS\Core\Session\UserSession;
39
use TYPO3\CMS\Core\Session\UserSessionManager;
40
use TYPO3\CMS\Core\SysLog\Action\Login as SystemLogLoginAction;
41
use TYPO3\CMS\Core\SysLog\Error as SystemLogErrorClassification;
42
use TYPO3\CMS\Core\SysLog\Type as SystemLogType;
43
use TYPO3\CMS\Core\Utility\GeneralUtility;
44
45
/**
46
 * Authentication of users in TYPO3
47
 *
48
 * This class is used to authenticate a login user.
49
 * The class is used by both the frontend and backend.
50
 * In both cases this class is a parent class to BackendUserAuthentication and FrontendUserAuthentication
51
 *
52
 * See Inside TYPO3 for more information about the API of the class and internal variables.
53
 */
54
abstract class AbstractUserAuthentication implements LoggerAwareInterface
55
{
56
    use LoggerAwareTrait;
57
    use CookieHeaderTrait;
58
59
    /**
60
     * Session/Cookie name
61
     * @var string
62
     */
63
    public $name = '';
64
65
    /**
66
     * Table in database with user data
67
     * @var string
68
     */
69
    public $user_table = '';
70
71
    /**
72
     * Table in database with user groups
73
     * @var string
74
     */
75
    public $usergroup_table = '';
76
77
    /**
78
     * Column for login-name
79
     * @var string
80
     */
81
    public $username_column = '';
82
83
    /**
84
     * Column for password
85
     * @var string
86
     */
87
    public $userident_column = '';
88
89
    /**
90
     * Column for user-id
91
     * @var string
92
     */
93
    public $userid_column = '';
94
95
    /**
96
     * Column for user group information
97
     * @var string
98
     */
99
    public $usergroup_column = '';
100
101
    /**
102
     * Column name for last login timestamp
103
     * @var string
104
     */
105
    public $lastLogin_column = '';
106
107
    /**
108
     * Enable field columns of user table
109
     * @var array
110
     */
111
    public $enablecolumns = [
112
        'rootLevel' => '',
113
        // Boolean: If TRUE, 'AND pid=0' will be a part of the query...
114
        'disabled' => '',
115
        'starttime' => '',
116
        'endtime' => '',
117
        'deleted' => '',
118
    ];
119
120
    /**
121
     * Form field with login-name
122
     * @var string
123
     */
124
    public $formfield_uname = '';
125
126
    /**
127
     * Form field with password
128
     * @var string
129
     */
130
    public $formfield_uident = '';
131
132
    /**
133
     * Form field with status: *'login', 'logout'. If empty login is not verified.
134
     * @var string
135
     */
136
    public $formfield_status = '';
137
138
    /**
139
     * Lifetime for the session-cookie (on the client)
140
     *
141
     * If >0: permanent cookie with given lifetime
142
     * If 0: session-cookie
143
     * Session-cookie means the browser will remove it when the browser is closed.
144
     *
145
     * @var int
146
     */
147
    protected $lifetime = 0;
148
149
    /**
150
     * Decides if the writelog() function is called at login and logout
151
     * @var bool
152
     */
153
    public $writeStdLog = false;
154
155
    /**
156
     * Log failed login attempts
157
     * @var bool
158
     */
159
    public $writeAttemptLog = false;
160
161
    /**
162
     * If set, the user-record must be stored at the page defined by $checkPid_value
163
     * @var bool
164
     */
165
    public $checkPid = true;
166
167
    /**
168
     * The page id the user record must be stored at
169
     * @var int
170
     */
171
    public $checkPid_value = 0;
172
173
    /**
174
     * Will be set to TRUE if the login session is actually written during auth-check.
175
     * @var bool
176
     */
177
    public $loginSessionStarted = false;
178
179
    /**
180
     * @var array|null contains user- AND session-data from database (joined tables)
181
     * @internal
182
     */
183
    public $user;
184
185
    /**
186
     * This array will hold the groups that the user is a member of
187
     */
188
    public array $userGroups = [];
189
190
    /**
191
     * Will prevent the setting of the session cookie
192
     * @var bool
193
     */
194
    public $dontSetCookie = false;
195
196
    /**
197
     * Login type, used for services.
198
     * @var string
199
     */
200
    public $loginType = '';
201
202
    /**
203
     * @var array
204
     */
205
    public $uc;
206
207
    protected ?UserSession $userSession;
208
209
    protected UserSessionManager $userSessionManager;
210
211
    /**
212
     * If set, this cookie will be set to the response.
213
     *
214
     * @var Cookie|null
215
     */
216
    protected ?Cookie $setCookie = null;
217
218
    /**
219
     * Initialize some important variables
220
     *
221
     * @throws Exception
222
     */
223
    public function __construct()
224
    {
225
        // Backend or frontend login - used for auth services
226
        if (empty($this->loginType)) {
227
            throw new Exception('No loginType defined, must be set explicitly by subclass', 1476045345);
228
        }
229
        $this->lifetime = (int)($GLOBALS['TYPO3_CONF_VARS'][$this->loginType]['lifetime'] ?? 0);
230
    }
231
232
    /**
233
     * Currently needed for various unit tests, until start() and checkAuthentication() methods
234
     * are smaller and extracted from this class.
235
     *
236
     * @param UserSessionManager|null $userSessionManager
237
     * @internal
238
     */
239
    public function initializeUserSessionManager(?UserSessionManager $userSessionManager = null): void
240
    {
241
        $this->userSessionManager = $userSessionManager ?? UserSessionManager::create($this->loginType);
242
        $this->userSession = $this->userSessionManager->createAnonymousSession();
243
    }
244
245
    /**
246
     * Starts a user session
247
     * Typical configurations will:
248
     * a) check if session cookie was set and if not, set one,
249
     * b) check if a password/username was sent and if so, try to authenticate the user
250
     * c) Lookup a session attached to a user and check timeout etc.
251
     * d) Garbage collection, setting of no-cache headers.
252
     * If a user is authenticated the database record of the user (array) will be set in the ->user internal variable.
253
     */
254
    public function start()
255
    {
256
        $this->logger->debug('## Beginning of auth logging.');
257
        // Make certain that NO user is set initially
258
        $this->user = null;
259
260
        if (!isset($this->userSessionManager)) {
261
            $this->initializeUserSessionManager();
262
        }
263
        $this->userSession = $this->userSessionManager->createFromGlobalCookieOrAnonymous($this->name);
264
265
        // Load user session, check to see if anyone has submitted login-information and if so authenticate
266
        // the user with the session. $this->user[uid] may be used to write log...
267
        try {
268
            $this->checkAuthentication();
269
        } catch (MfaRequiredException $mfaRequiredException) {
270
            // Ensure the cookie is still set to keep the user session available
271
            if (!$this->dontSetCookie || $this->isRefreshTimeBasedCookie()) {
272
                $this->setSessionCookie();
273
            }
274
            throw $mfaRequiredException;
275
        }
276
        // Set cookie if generally enabled or if the current session is a non-session cookie (FE permalogin)
277
        if (!$this->dontSetCookie || $this->isRefreshTimeBasedCookie()) {
278
            $this->setSessionCookie();
279
        }
280
        // Hook for alternative ways of filling the $this->user array (is used by the "timtaw" extension)
281
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['postUserLookUp'] ?? [] as $funcName) {
282
            $_params = [
283
                'pObj' => $this,
284
            ];
285
            GeneralUtility::callUserFunction($funcName, $_params, $this);
286
        }
287
    }
288
289
    /**
290
     * Used to apply a cookie to a PSR-7 Response.
291
     *
292
     * @param ResponseInterface $response
293
     * @return ResponseInterface
294
     */
295
    public function appendCookieToResponse(ResponseInterface $response): ResponseInterface
296
    {
297
        if ($this->setCookie !== null) {
298
            $response = $response->withAddedHeader('Set-Cookie', $this->setCookie->__toString());
299
        }
300
        return $response;
301
    }
302
303
    /**
304
     * Sets the session cookie for the current disposal.
305
     */
306
    protected function setSessionCookie()
307
    {
308
        $isRefreshTimeBasedCookie = $this->isRefreshTimeBasedCookie();
309
        if ($this->isSetSessionCookie() || $isRefreshTimeBasedCookie) {
310
            // Get the domain to be used for the cookie (if any):
311
            $cookieDomain = $this->getCookieDomain();
312
            // If no cookie domain is set, use the base path:
313
            $cookiePath = $cookieDomain ? '/' : GeneralUtility::getIndpEnv('TYPO3_SITE_PATH');
314
            // If the cookie lifetime is set, use it:
315
            $cookieExpire = $isRefreshTimeBasedCookie ? $GLOBALS['EXEC_TIME'] + $this->lifetime : 0;
316
            // Valid options are "strict", "lax" or "none", whereas "none" only works in HTTPS requests (default & fallback is "strict")
317
            $cookieSameSite = $this->sanitizeSameSiteCookieValue(
318
                strtolower($GLOBALS['TYPO3_CONF_VARS'][$this->loginType]['cookieSameSite'] ?? Cookie::SAMESITE_STRICT)
319
            );
320
            // Use the secure option when the current request is served by a secure connection:
321
            // SameSite "none" needs the secure option (only allowed on HTTPS)
322
            $isSecure = $cookieSameSite === Cookie::SAMESITE_NONE || GeneralUtility::getIndpEnv('TYPO3_SSL');
323
            $sessionId = $this->userSession->getIdentifier();
0 ignored issues
show
Bug introduced by
The method getIdentifier() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

323
            /** @scrutinizer ignore-call */ 
324
            $sessionId = $this->userSession->getIdentifier();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
324
            $this->setCookie = new Cookie(
325
                $this->name,
326
                $sessionId,
327
                $cookieExpire,
328
                $cookiePath,
329
                $cookieDomain,
330
                $isSecure,
331
                true,
332
                false,
333
                $cookieSameSite
334
            );
335
            $this->logger->debug(
336
                ($isRefreshTimeBasedCookie ? 'Updated Cookie: ' : 'Set Cookie: ')
337
                . $sessionId . ($cookieDomain ? ', ' . $cookieDomain : '')
338
            );
339
        }
340
    }
341
342
    /**
343
     * Gets the domain to be used on setting cookies.
344
     * The information is taken from the value in $GLOBALS['TYPO3_CONF_VARS']['SYS']['cookieDomain'].
345
     *
346
     * @return string The domain to be used on setting cookies
347
     */
348
    protected function getCookieDomain()
349
    {
350
        $result = '';
351
        $cookieDomain = $GLOBALS['TYPO3_CONF_VARS']['SYS']['cookieDomain'];
352
        // If a specific cookie domain is defined for a given application type, use that domain
353
        if (!empty($GLOBALS['TYPO3_CONF_VARS'][$this->loginType]['cookieDomain'])) {
354
            $cookieDomain = $GLOBALS['TYPO3_CONF_VARS'][$this->loginType]['cookieDomain'];
355
        }
356
        if ($cookieDomain) {
357
            if ($cookieDomain[0] === '/') {
358
                $match = [];
359
                $matchCnt = @preg_match($cookieDomain, GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY'), $match);
360
                if ($matchCnt === false) {
361
                    $this->logger->critical('The regular expression for the cookie domain (' . $cookieDomain . ') contains errors. The session is not shared across sub-domains.');
362
                } elseif ($matchCnt) {
363
                    $result = $match[0];
364
                }
365
            } else {
366
                $result = $cookieDomain;
367
            }
368
        }
369
        return $result;
370
    }
371
372
    /**
373
     * Get the value of a specified cookie.
374
     *
375
     * @param string $cookieName The cookie ID
376
     * @return string The value stored in the cookie
377
     */
378
    protected function getCookie($cookieName)
379
    {
380
        return isset($_COOKIE[$cookieName]) ? stripslashes($_COOKIE[$cookieName]) : '';
381
    }
382
383
    /**
384
     * Determine whether a session cookie needs to be set (lifetime=0)
385
     *
386
     * @return bool
387
     * @internal
388
     */
389
    public function isSetSessionCookie()
390
    {
391
        return $this->userSession->isNew() && $this->lifetime === 0;
392
    }
393
394
    /**
395
     * Determine whether a non-session cookie needs to be set (lifetime>0)
396
     *
397
     * @return bool
398
     * @internal
399
     */
400
    public function isRefreshTimeBasedCookie()
401
    {
402
        return $this->lifetime > 0;
403
    }
404
405
    /**
406
     * "auth" services configuration array from $GLOBALS['TYPO3_CONF_VARS']['SVCONF']['auth']
407
     * @return array
408
     */
409
    protected function getAuthServiceConfiguration(): array
410
    {
411
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['SVCONF']['auth']['setup'] ?? null)) {
412
            return $GLOBALS['TYPO3_CONF_VARS']['SVCONF']['auth']['setup'];
413
        }
414
        return [];
415
    }
416
417
    /**
418
     * Checks if a submission of username and password is present or use other authentication by auth services
419
     *
420
     * @throws \RuntimeException
421
     * @internal
422
     */
423
    public function checkAuthentication()
424
    {
425
        $authConfiguration = $this->getAuthServiceConfiguration();
426
        if (!empty($authConfiguration)) {
427
            $this->logger->debug('Authentication Service Configuration found.', $authConfiguration);
428
        }
429
        // No user for now - will be searched by service below
430
        $tempuserArr = [];
431
        $tempuser = false;
432
        // User is not authenticated by default
433
        $authenticated = false;
434
        // User want to login with passed login data (name/password)
435
        $activeLogin = false;
436
        $this->logger->debug('Login type: ' . $this->loginType);
437
        // The info array provide additional information for auth services
438
        $authInfo = $this->getAuthInfoArray();
439
        // Get Login/Logout data submitted by a form or params
440
        $loginData = $this->getLoginFormData();
441
        $this->logger->debug('Login data', $loginData);
442
        // Active logout (eg. with "logout" button)
443
        if ($loginData['status'] === LoginType::LOGOUT) {
444
            if ($this->writeStdLog) {
445
                // $type,$action,$error,$details_nr,$details,$data,$tablename,$recuid,$recpid
446
                $this->writelog(SystemLogType::LOGIN, SystemLogLoginAction::LOGOUT, SystemLogErrorClassification::MESSAGE, 2, 'User %s logged out', [$this->user['username']], '', 0, 0);
447
            }
448
            $this->logger->info('User logged out. Id: ' . $this->userSession->getIdentifier());
449
            $this->logoff();
450
        }
451
        // Determine whether we need to skip session update.
452
        // This is used mainly for checking session timeout in advance without refreshing the current session's timeout.
453
        $skipSessionUpdate = (bool)GeneralUtility::_GP('skipSessionUpdate');
454
        $haveSession = false;
455
        $anonymousSession = false;
456
        if (!$this->userSession->isNew()) {
457
            // Read user data if this is bound to a user
458
            // However, if the user data is not valid, or the session has timed out we'll recreate a new anonymous session
459
            if ($this->userSession->getUserId() > 0) {
460
                $authInfo['user'] = $this->fetchValidUserFromSessionOrDestroySession($skipSessionUpdate);
461
                if (is_array($authInfo['user'])) {
462
                    $authInfo['userSession'] = $authInfo['user'];
463
                } else {
464
                    $authInfo['userSession'] = false;
465
                }
466
            }
467
            $authInfo['session'] = $this->userSession;
468
            $haveSession = !$this->userSession->isNew();
469
            $anonymousSession = $haveSession && $this->userSession->isAnonymous();
470
        }
471
472
        // Active login (eg. with login form).
473
        if (!$haveSession && $loginData['status'] === LoginType::LOGIN) {
474
            $activeLogin = true;
475
            $this->logger->debug('Active login (eg. with login form)');
476
            // check referrer for submitted login values
477
            if ($this->formfield_status && $loginData['uident'] && $loginData['uname']) {
478
                // Delete old user session if any
479
                $this->logoff();
480
            }
481
            // Refuse login for _CLI users, if not processing a CLI request type
482
            // (although we shouldn't be here in case of a CLI request type)
483
            if (stripos($loginData['uname'], '_CLI_') === 0 && !Environment::isCli()) {
484
                throw new \RuntimeException('TYPO3 Fatal Error: You have tried to login using a CLI user. Access prohibited!', 1270853931);
485
            }
486
        }
487
488
        // Cause elevation of privilege, make sure regenerateSessionId is called later on
489
        if ($anonymousSession && $loginData['status'] === LoginType::LOGIN) {
490
            $activeLogin = true;
491
        }
492
493
        if ($haveSession) {
494
            $this->logger->debug('User found in session', [
495
                $this->userid_column => $authInfo['user'][$this->userid_column] ?? null,
496
                $this->username_column => $authInfo['user'][$this->username_column] ?? null,
497
            ]);
498
        } else {
499
            $this->logger->debug('No user session found');
500
        }
501
502
        // Fetch user if ...
503
        if (
504
            $activeLogin || !empty($authConfiguration[$this->loginType . '_alwaysFetchUser'])
505
            || !$haveSession && !empty($authConfiguration[$this->loginType . '_fetchUserIfNoSession'])
506
        ) {
507
            // Use 'auth' service to find the user
508
            // First found user will be used
509
            $subType = 'getUser' . $this->loginType;
510
            /** @var AuthenticationService $serviceObj */
511
            foreach ($this->getAuthServices($subType, $loginData, $authInfo) as $serviceObj) {
512
                if ($row = $serviceObj->getUser()) {
513
                    $tempuserArr[] = $row;
514
                    $this->logger->debug('User found', [
515
                        $this->userid_column => $row[$this->userid_column],
516
                        $this->username_column => $row[$this->username_column],
517
                    ]);
518
                    // User found, just stop to search for more if not configured to go on
519
                    if (empty($authConfiguration[$this->loginType . '_fetchAllUsers'])) {
520
                        break;
521
                    }
522
                }
523
            }
524
525
            if (!empty($authConfiguration[$this->loginType . '_alwaysFetchUser'])) {
526
                $this->logger->debug($this->loginType . '_alwaysFetchUser option is enabled');
527
            }
528
            if (empty($tempuserArr)) {
529
                $this->logger->debug('No user found by services');
530
            } else {
531
                $this->logger->debug(count($tempuserArr) . ' user records found by services');
532
            }
533
        }
534
535
        // If no new user was set we use the already found user session
536
        if (empty($tempuserArr) && $haveSession && !$anonymousSession) {
537
            $tempuserArr[] = $authInfo['user'];
538
            $tempuser = $authInfo['user'];
539
            // User is authenticated because we found a user session
540
            $authenticated = true;
541
            $this->logger->debug('User session used', [
542
                $this->userid_column => $authInfo['user'][$this->userid_column],
543
                $this->username_column => $authInfo['user'][$this->username_column],
544
            ]);
545
        }
546
        // Re-auth user when 'auth'-service option is set
547
        if (!empty($authConfiguration[$this->loginType . '_alwaysAuthUser'])) {
548
            $authenticated = false;
549
            $this->logger->debug('alwaysAuthUser option is enabled');
550
        }
551
        // Authenticate the user if needed
552
        if (!empty($tempuserArr) && !$authenticated) {
553
            foreach ($tempuserArr as $tempuser) {
554
                // Use 'auth' service to authenticate the user
555
                // If one service returns FALSE then authentication failed
556
                // a service might return 100 which means there's no reason to stop but the user can't be authenticated by that service
557
                $this->logger->debug('Auth user', $tempuser);
558
                $subType = 'authUser' . $this->loginType;
559
560
                /** @var AuthenticationService $serviceObj */
561
                foreach ($this->getAuthServices($subType, $loginData, $authInfo) as $serviceObj) {
562
                    if (($ret = $serviceObj->authUser($tempuser)) > 0) {
563
                        // If the service returns >=200 then no more checking is needed - useful for IP checking without password
564
                        if ((int)$ret >= 200) {
565
                            $authenticated = true;
566
                            break;
567
                        }
568
                        if ((int)$ret >= 100) {
569
                        } else {
570
                            $authenticated = true;
571
                        }
572
                    } else {
573
                        $authenticated = false;
574
                        break;
575
                    }
576
                }
577
578
                if ($authenticated) {
579
                    // Leave foreach() because a user is authenticated
580
                    break;
581
                }
582
            }
583
        }
584
585
        // If user is authenticated a valid user is in $tempuser
586
        if ($authenticated) {
587
            // Insert session record if needed:
588
            if (!$haveSession
589
                || $anonymousSession
590
                || (int)$tempuser['uid'] !== $this->userSession->getUserId()
591
            ) {
592
                $sessionData = $this->userSession->getData();
593
                // Create a new session with a fixated user
594
                $this->userSession = $this->createUserSession($tempuser);
595
596
                // Preserve session data on login
597
                if ($anonymousSession || $haveSession) {
598
                    $this->userSession->overrideData($sessionData);
599
                }
600
601
                $this->user = array_merge($tempuser, $this->user ?? []);
602
603
                // The login session is started.
604
                $this->loginSessionStarted = true;
605
                if (is_array($this->user)) {
0 ignored issues
show
introduced by
The condition is_array($this->user) is always true.
Loading history...
606
                    $this->logger->debug('User session finally read', [
607
                        $this->userid_column => $this->user[$this->userid_column],
608
                        $this->username_column => $this->user[$this->username_column],
609
                    ]);
610
                }
611
            } else {
612
                // if we come here the current session is for sure not anonymous as this is a pre-condition for $authenticated = true
613
                $this->user = $authInfo['user'];
614
            }
615
616
            if ($activeLogin && !$this->userSession->isNew()) {
617
                $this->regenerateSessionId();
618
            }
619
620
            // Check if multi-factor authentication is required
621
            $this->evaluateMfaRequirements();
622
623
            if ($activeLogin) {
624
                // User logged in - write that to the log!
625
                if ($this->writeStdLog) {
626
                    $this->writelog(SystemLogType::LOGIN, SystemLogLoginAction::LOGIN, SystemLogErrorClassification::MESSAGE, 1, 'User %s logged in from ###IP###', [$tempuser[$this->username_column]], '', '', '');
627
                }
628
                $this->logger->info('User ' . $tempuser[$this->username_column] . ' logged in from ' . GeneralUtility::getIndpEnv('REMOTE_ADDR'));
629
            } else {
630
                $this->logger->debug('User ' . $tempuser[$this->username_column] . ' authenticated from ' . GeneralUtility::getIndpEnv('REMOTE_ADDR'));
631
            }
632
        } else {
633
            // Mark the current login attempt as failed
634
            if (empty($tempuserArr) && $activeLogin) {
635
                $this->logger->debug('Login failed', [
636
                    'loginData' => $loginData
637
                ]);
638
            } elseif (!empty($tempuserArr)) {
639
                $this->logger->debug('Login failed', [
640
                    $this->userid_column => $tempuser[$this->userid_column],
641
                    $this->username_column => $tempuser[$this->username_column],
642
                ]);
643
            }
644
645
            // If there were a login failure, check to see if a warning email should be sent
646
            if ($activeLogin) {
647
                $this->handleLoginFailure();
648
            }
649
        }
650
    }
651
652
    /**
653
     * This method checks if the user is authenticated but has not succeeded in
654
     * passing his MFA challenge. This method can therefore only be used if a user
655
     * has been authenticated against his first authentication method (username+password
656
     * or any other authentication token).
657
     *
658
     * @throws MfaRequiredException
659
     * @internal
660
     */
661
    protected function evaluateMfaRequirements(): void
662
    {
663
        // MFA has been validated already, nothing to do
664
        if ($this->getSessionData('mfa')) {
665
            return;
666
        }
667
        // If the user session does not contain the 'mfa' key - indicating that MFA is already
668
        // passed - get the first provider for authentication, which is either the default provider
669
        // or the first active provider (based on the providers configured ordering).
670
        $provider = GeneralUtility::makeInstance(MfaProviderRegistry::class)->getFirstAuthenticationAwareProvider($this);
671
        // Throw an exception (hopefully called in a middleware) when an active provider for the user exists
672
        if ($provider !== null) {
673
            throw new MfaRequiredException($provider, 1613687097);
674
        }
675
        // @todo If user has no active providers, check if the user is required to
676
        //       setup MFA and redirect to a standalone registration controller.
677
        //       Currently we just let the user proceed to its original target.
678
    }
679
680
    /**
681
     * Implement functionality when there was a failed login
682
     */
683
    protected function handleLoginFailure(): void
684
    {
685
        $_params = [];
686
        $sleep = true;
687
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['postLoginFailureProcessing'] ?? [] as $hookIdentifier => $_funcRef) {
688
            GeneralUtility::callUserFunction($_funcRef, $_params, $this);
689
            // This hack will be removed once this is migrated into PSR-14 Events
690
            if ($hookIdentifier !== 'sendEmailOnFailedLoginAttempt') {
691
                $sleep = false;
692
            }
693
        }
694
        if ($sleep) {
695
            // No hooks were triggered - default login failure behavior is to sleep 5 seconds
696
            sleep(5);
697
        }
698
    }
699
700
    /**
701
     * Creates a new session ID.
702
     *
703
     * @return string The new session ID
704
     * @deprecated since TYPO3 v11.0, will be removed in TYPO3 v12, is kept because it is used in Testing Framework
705
     */
706
    public function createSessionId()
707
    {
708
        return GeneralUtility::makeInstance(Random::class)->generateRandomHexString(32);
709
    }
710
711
    /**
712
     * Initializes authentication services to be used in a foreach loop
713
     *
714
     * @param string $subType e.g. getUserFE
715
     * @param array $loginData
716
     * @param array $authInfo
717
     * @return \Traversable A generator of service objects
718
     */
719
    protected function getAuthServices(string $subType, array $loginData, array $authInfo): \Traversable
720
    {
721
        $serviceChain = [];
722
        while (is_object($serviceObj = GeneralUtility::makeInstanceService('auth', $subType, $serviceChain))) {
723
            $serviceChain[] = $serviceObj->getServiceKey();
724
            $serviceObj->initAuth($subType, $loginData, $authInfo, $this);
725
            yield $serviceObj;
726
        }
727
        if (!empty($serviceChain)) {
728
            $this->logger->debug($subType . ' auth services called: ' . implode(', ', $serviceChain));
729
        }
730
    }
731
732
    /**
733
     * Regenerate the session ID and transfer the session to new ID
734
     * Call this method whenever a user proceeds to a higher authorization level
735
     * e.g. when an anonymous session is now authenticated.
736
     */
737
    protected function regenerateSessionId()
738
    {
739
        $this->userSession = $this->userSessionManager->regenerateSession($this->userSession->getIdentifier());
740
    }
741
742
    /*************************
743
     *
744
     * User Sessions
745
     *
746
     *************************/
747
748
    /**
749
     * Creates a user session record and returns its values.
750
     *
751
     * @param array $tempuser User data array
752
     * @return UserSession The session data for the newly created session.
753
     */
754
    public function createUserSession(array $tempuser): UserSession
755
    {
756
        // Needed for testing framework
757
        if (!isset($this->userSessionManager)) {
758
            $this->initializeUserSessionManager();
759
        }
760
        $tempUserId = (int)($tempuser[$this->userid_column] ?? 0);
761
        $session = $this->userSessionManager->elevateToFixatedUserSession($this->userSession, $tempUserId);
0 ignored issues
show
Bug introduced by
It seems like $this->userSession can also be of type null; however, parameter $session of TYPO3\CMS\Core\Session\U...eToFixatedUserSession() does only seem to accept TYPO3\CMS\Core\Session\UserSession, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

761
        $session = $this->userSessionManager->elevateToFixatedUserSession(/** @scrutinizer ignore-type */ $this->userSession, $tempUserId);
Loading history...
762
        // Updating lastLogin_column carrying information about last login.
763
        $this->updateLoginTimestamp($tempUserId);
764
        return $session;
765
    }
766
767
    /**
768
     * Updates the last login column in the user with the given id
769
     *
770
     * @param int $userId
771
     */
772
    protected function updateLoginTimestamp(int $userId)
773
    {
774
        if ($this->lastLogin_column) {
775
            $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->user_table);
776
            $connection->update(
777
                $this->user_table,
778
                [$this->lastLogin_column => $GLOBALS['EXEC_TIME']],
779
                [$this->userid_column => $userId]
780
            );
781
            $this->user[$this->lastLogin_column] = $GLOBALS['EXEC_TIME'];
782
        }
783
    }
784
785
    /**
786
     * Read the user session from db.
787
     *
788
     * @param bool $skipSessionUpdate
789
     * @return array|bool User session data, false if $userSession->getIdentifier() does not represent valid session
790
     * @deprecated since TYPO3 v11, will be removed in TYPO3 v12.
791
     */
792
    public function fetchUserSession($skipSessionUpdate = false)
793
    {
794
        try {
795
            $session = $this->userSessionManager->createSessionFromStorage($this->userSession->getIdentifier());
796
        } catch (SessionNotFoundException $e) {
797
            return false;
798
        }
799
        $this->userSession = $session;
800
        // Session is anonymous so no need to fetch user
801
        if ($session->isAnonymous()) {
802
            return $session->toArray();
803
        }
804
805
        // Fetch the user from the DB
806
        $userRecord = $this->fetchValidUserFromSessionOrDestroySession($skipSessionUpdate);
807
        return is_array($userRecord) ? $userRecord : false;
808
    }
809
810
    /**
811
     * If the session is bound to a user, this method fetches the user record, and returns it.
812
     * If the session has a timeout, the session date is extended if needed. Also the ìs_online
813
     * flag is updated for the user.
814
     *
815
     * However, if the session has expired the session is removed and the request is treated as an anonymous session.
816
     *
817
     * @param bool $skipSessionUpdate
818
     * @return array|null
819
     */
820
    protected function fetchValidUserFromSessionOrDestroySession(bool $skipSessionUpdate = false): ?array
821
    {
822
        if ($this->userSession->isAnonymous()) {
823
            return null;
824
        }
825
        // Fetch the user from the DB
826
        $userRecord = $this->getRawUserByUid($this->userSession->getUserId());
827
        if ($userRecord) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $userRecord of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
828
            // A user was found
829
            $userRecord['is_online'] = $this->userSession->getLastUpdated();
830
            if (!$this->userSessionManager->hasExpired($this->userSession)) {
0 ignored issues
show
Bug introduced by
It seems like $this->userSession can also be of type null; however, parameter $session of TYPO3\CMS\Core\Session\U...onManager::hasExpired() does only seem to accept TYPO3\CMS\Core\Session\UserSession, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

830
            if (!$this->userSessionManager->hasExpired(/** @scrutinizer ignore-type */ $this->userSession)) {
Loading history...
831
                if (!$skipSessionUpdate) {
832
                    $this->userSessionManager->updateSessionTimestamp($this->userSession);
0 ignored issues
show
Bug introduced by
It seems like $this->userSession can also be of type null; however, parameter $session of TYPO3\CMS\Core\Session\U...pdateSessionTimestamp() does only seem to accept TYPO3\CMS\Core\Session\UserSession, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

832
                    $this->userSessionManager->updateSessionTimestamp(/** @scrutinizer ignore-type */ $this->userSession);
Loading history...
833
                }
834
            } else {
835
                // Delete any user set...
836
                $this->logoff();
837
                $userRecord = false;
838
                $this->userSession = $this->userSessionManager->createAnonymousSession();
839
            }
840
        }
841
        return is_array($userRecord) ? $userRecord : null;
842
    }
843
844
    /**
845
     * Regenerates the session ID and sets the cookie again.
846
     *
847
     * @internal
848
     */
849
    public function enforceNewSessionId()
850
    {
851
        $this->regenerateSessionId();
852
        $this->setSessionCookie();
853
    }
854
855
    /**
856
     * Log out current user!
857
     * Removes the current session record, sets the internal ->user array to a blank string;
858
     * Thereby the current user (if any) is effectively logged out!
859
     */
860
    public function logoff()
861
    {
862
        $this->logger->debug('logoff: ses_id = ' . $this->userSession->getIdentifier());
863
864
        $_params = [];
865
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'] ?? [] as $_funcRef) {
866
            if ($_funcRef) {
867
                GeneralUtility::callUserFunction($_funcRef, $_params, $this);
868
            }
869
        }
870
        $this->performLogoff();
0 ignored issues
show
Bug introduced by
The method performLogoff() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

870
        $this->/** @scrutinizer ignore-call */ 
871
               performLogoff();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
871
872
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_post_processing'] ?? [] as $_funcRef) {
873
            if ($_funcRef) {
874
                GeneralUtility::callUserFunction($_funcRef, $_params, $this);
875
            }
876
        }
877
    }
878
879
    /**
880
     * Perform the logoff action. Called from logoff() as a way to allow subclasses to override
881
     * what happens when a user logs off, without needing to reproduce the hook calls and logging
882
     * that happens in the public logoff() API method.
883
     */
884
    protected function performLogoff()
885
    {
886
        if ($this->userSession) {
887
            $this->userSessionManager->removeSession($this->userSession);
888
        }
889
        $this->userSession = $this->userSessionManager->createAnonymousSession();
890
        $this->user = null;
891
        if ($this->isCookieSet()) {
892
            $this->removeCookie($this->name);
893
        }
894
    }
895
896
    /**
897
     * Empty / unset the cookie
898
     *
899
     * @param string|null $cookieName usually, this is $this->name
900
     */
901
    public function removeCookie($cookieName = null)
902
    {
903
        $cookieName = $cookieName ?? $this->name;
904
        $cookieDomain = $this->getCookieDomain();
905
        // If no cookie domain is set, use the base path
906
        $cookiePath = $cookieDomain ? '/' : GeneralUtility::getIndpEnv('TYPO3_SITE_PATH');
907
        $this->setCookie = new Cookie(
908
            $cookieName,
909
            '',
910
            -1,
911
            $cookiePath,
912
            $cookieDomain
913
        );
914
    }
915
916
    /**
917
     * Returns whether this request is going to set a cookie
918
     * or a cookie was already found in the system
919
     *
920
     * @return bool Returns TRUE if a cookie is set
921
     */
922
    public function isCookieSet()
923
    {
924
        return isset($this->setCookie) || $this->getCookie($this->name);
925
    }
926
927
    /*************************
928
     *
929
     * SQL Functions
930
     *
931
     *************************/
932
    /**
933
     * This returns the restrictions needed to select the user respecting
934
     * enable columns and flags like deleted, hidden, starttime, endtime
935
     * and rootLevel
936
     *
937
     * @return QueryRestrictionContainerInterface
938
     * @internal
939
     */
940
    protected function userConstraints(): QueryRestrictionContainerInterface
941
    {
942
        $restrictionContainer = GeneralUtility::makeInstance(DefaultRestrictionContainer::class);
943
944
        if (empty($this->enablecolumns['disabled'])) {
945
            $restrictionContainer->removeByType(HiddenRestriction::class);
946
        }
947
948
        if (empty($this->enablecolumns['deleted'])) {
949
            $restrictionContainer->removeByType(DeletedRestriction::class);
950
        }
951
952
        if (empty($this->enablecolumns['starttime'])) {
953
            $restrictionContainer->removeByType(StartTimeRestriction::class);
954
        }
955
956
        if (empty($this->enablecolumns['endtime'])) {
957
            $restrictionContainer->removeByType(EndTimeRestriction::class);
958
        }
959
960
        if (!empty($this->enablecolumns['rootLevel'])) {
961
            $restrictionContainer->add(GeneralUtility::makeInstance(RootLevelRestriction::class, [$this->user_table]));
962
        }
963
964
        return $restrictionContainer;
965
    }
966
967
    /*************************
968
     *
969
     * Session and Configuration Handling
970
     *
971
     *************************/
972
    /**
973
     * This writes $variable to the user-record. This is a way of providing session-data.
974
     * You can fetch the data again through $this->uc in this class!
975
     * If $variable is not an array, $this->uc is saved!
976
     *
977
     * @param array|string $variable An array you want to store for the user as session data. If $variable is not supplied (is null), the internal variable, ->uc, is stored by default
978
     */
979
    public function writeUC($variable = '')
980
    {
981
        if (is_array($this->user) && $this->user[$this->userid_column]) {
982
            if (!is_array($variable)) {
983
                $variable = $this->uc;
984
            }
985
            $this->logger->debug('writeUC: ' . $this->userid_column . '=' . (int)$this->user[$this->userid_column]);
986
            GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->user_table)->update(
987
                $this->user_table,
988
                ['uc' => serialize($variable)],
989
                [$this->userid_column => (int)$this->user[$this->userid_column]],
990
                ['uc' => Connection::PARAM_LOB]
991
            );
992
        }
993
    }
994
995
    /**
996
     * Sets $theUC as the internal variable ->uc IF $theUC is an array.
997
     * If $theUC is FALSE, the 'uc' content from the ->user array will be unserialized and restored in ->uc
998
     *
999
     * @param mixed $theUC If an array, then set as ->uc, otherwise load from user record
1000
     */
1001
    public function unpack_uc($theUC = '')
1002
    {
1003
        if (!$theUC && isset($this->user['uc'])) {
1004
            $theUC = unserialize($this->user['uc'], ['allowed_classes' => false]);
1005
        }
1006
        if (is_array($theUC)) {
1007
            $this->uc = $theUC;
1008
        }
1009
    }
1010
1011
    /**
1012
     * Stores data for a module.
1013
     * The data is stored with the session id so you can even check upon retrieval
1014
     * if the module data is from a previous session or from the current session.
1015
     *
1016
     * @param string $module Is the name of the module ($MCONF['name'])
1017
     * @param mixed $data Is the data you want to store for that module (array, string, ...)
1018
     * @param bool|int $noSave If $noSave is set, then the ->uc array (which carries all kinds of user data) is NOT written immediately, but must be written by some subsequent call.
1019
     */
1020
    public function pushModuleData($module, $data, $noSave = 0)
1021
    {
1022
        $this->uc['moduleData'][$module] = $data;
1023
        $this->uc['moduleSessionID'][$module] = $this->userSession->getIdentifier();
1024
        if (!$noSave) {
1025
            $this->writeUC();
1026
        }
1027
    }
1028
1029
    /**
1030
     * Gets module data for a module (from a loaded ->uc array)
1031
     *
1032
     * @param string $module Is the name of the module ($MCONF['name'])
1033
     * @param string $type If $type = 'ses' then module data is returned only if it was stored in the current session, otherwise data from a previous session will be returned (if available).
1034
     * @return mixed The module data if available: $this->uc['moduleData'][$module];
1035
     */
1036
    public function getModuleData($module, $type = '')
1037
    {
1038
        if ($type !== 'ses' || (isset($this->uc['moduleSessionID'][$module]) && $this->uc['moduleSessionID'][$module] == $this->userSession->getIdentifier())) {
1039
            return $this->uc['moduleData'][$module];
1040
        }
1041
        return null;
1042
    }
1043
1044
    /**
1045
     * Returns the session data stored for $key.
1046
     * The data will last only for this login session since it is stored in the user session.
1047
     *
1048
     * @param string $key The key associated with the session data
1049
     * @return mixed
1050
     */
1051
    public function getSessionData($key)
1052
    {
1053
        return $this->userSession->get($key);
1054
    }
1055
1056
    /**
1057
     * Set session data by key.
1058
     * The data will last only for this login session since it is stored in the user session.
1059
     *
1060
     * @param string $key A non empty string to store the data under
1061
     * @param mixed $data Data store store in session
1062
     */
1063
    public function setSessionData($key, $data)
1064
    {
1065
        $this->userSession->set($key, $data);
1066
    }
1067
1068
    /**
1069
     * Sets the session data ($data) for $key and writes all session data (from ->user['ses_data']) to the database.
1070
     * The data will last only for this login session since it is stored in the session table.
1071
     *
1072
     * @param string $key Pointer to an associative key in the session data array which is stored serialized in the field "ses_data" of the session table.
1073
     * @param mixed $data The data to store in index $key
1074
     */
1075
    public function setAndSaveSessionData($key, $data)
1076
    {
1077
        $this->userSession->set($key, $data);
1078
        $this->logger->debug('setAndSaveSessionData: ses_id = ' . $this->userSession->getIdentifier());
1079
        $this->userSessionManager->updateSession($this->userSession);
0 ignored issues
show
Bug introduced by
It seems like $this->userSession can also be of type null; however, parameter $session of TYPO3\CMS\Core\Session\U...anager::updateSession() does only seem to accept TYPO3\CMS\Core\Session\UserSession, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1079
        $this->userSessionManager->updateSession(/** @scrutinizer ignore-type */ $this->userSession);
Loading history...
1080
    }
1081
1082
    /*************************
1083
     *
1084
     * Misc
1085
     *
1086
     *************************/
1087
    /**
1088
     * Returns an info array with Login/Logout data submitted by a form or params
1089
     *
1090
     * @return array
1091
     * @internal
1092
     */
1093
    public function getLoginFormData()
1094
    {
1095
        $loginData = [
1096
            'status' => GeneralUtility::_GP($this->formfield_status),
1097
            'uname'  => GeneralUtility::_POST($this->formfield_uname),
1098
            'uident' => GeneralUtility::_POST($this->formfield_uident)
1099
        ];
1100
        // Only process the login data if a login is requested
1101
        if ($loginData['status'] === LoginType::LOGIN) {
1102
            $loginData = $this->processLoginData($loginData);
1103
        }
1104
        return $loginData;
1105
    }
1106
1107
    /**
1108
     * Processes Login data submitted by a form or params depending on the
1109
     * passwordTransmissionStrategy
1110
     *
1111
     * @param array $loginData Login data array
1112
     * @param string $passwordTransmissionStrategy Alternative passwordTransmissionStrategy. Used when authentication services wants to override the default.
1113
     * @return array
1114
     * @internal
1115
     */
1116
    public function processLoginData($loginData, $passwordTransmissionStrategy = '')
1117
    {
1118
        $loginSecurityLevel = trim($GLOBALS['TYPO3_CONF_VARS'][$this->loginType]['loginSecurityLevel']) ?: 'normal';
1119
        $passwordTransmissionStrategy = $passwordTransmissionStrategy ?: $loginSecurityLevel;
1120
        $this->logger->debug('Login data before processing', $loginData);
1121
        $subType = 'processLoginData' . $this->loginType;
1122
        $authInfo = $this->getAuthInfoArray();
1123
        $isLoginDataProcessed = false;
1124
        $processedLoginData = $loginData;
1125
        /** @var AuthenticationService $serviceObject */
1126
        foreach ($this->getAuthServices($subType, $loginData, $authInfo) as $serviceObject) {
1127
            $serviceResult = $serviceObject->processLoginData($processedLoginData, $passwordTransmissionStrategy);
1128
            if (!empty($serviceResult)) {
1129
                $isLoginDataProcessed = true;
1130
                // If the service returns >=200 then no more processing is needed
1131
                if ((int)$serviceResult >= 200) {
1132
                    break;
1133
                }
1134
            }
1135
        }
1136
        if ($isLoginDataProcessed) {
1137
            $loginData = $processedLoginData;
1138
            $this->logger->debug('Processed login data', $processedLoginData);
1139
        }
1140
        return $loginData;
1141
    }
1142
1143
    /**
1144
     * Returns an info array which provides additional information for auth services
1145
     *
1146
     * @return array
1147
     * @internal
1148
     */
1149
    public function getAuthInfoArray()
1150
    {
1151
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->user_table);
1152
        $expressionBuilder = $queryBuilder->expr();
1153
        $authInfo = [];
1154
        $authInfo['loginType'] = $this->loginType;
1155
        $authInfo['refInfo'] = parse_url(GeneralUtility::getIndpEnv('HTTP_REFERER'));
1156
        $authInfo['HTTP_HOST'] = GeneralUtility::getIndpEnv('HTTP_HOST');
1157
        $authInfo['REMOTE_ADDR'] = GeneralUtility::getIndpEnv('REMOTE_ADDR');
1158
        $authInfo['REMOTE_HOST'] = GeneralUtility::getIndpEnv('REMOTE_HOST');
1159
        // Can be overridden in localconf by SVCONF:
1160
        $authInfo['db_user']['table'] = $this->user_table;
1161
        $authInfo['db_user']['userid_column'] = $this->userid_column;
1162
        $authInfo['db_user']['username_column'] = $this->username_column;
1163
        $authInfo['db_user']['userident_column'] = $this->userident_column;
1164
        $authInfo['db_user']['enable_clause'] = $this->userConstraints()->buildExpression(
1165
            [$this->user_table => $this->user_table],
1166
            $expressionBuilder
1167
        );
1168
        if ($this->checkPid && $this->checkPid_value !== null) {
1169
            $authInfo['db_user']['checkPidList'] = $this->checkPid_value;
1170
            $authInfo['db_user']['check_pid_clause'] = $expressionBuilder->in(
1171
                'pid',
1172
                GeneralUtility::intExplode(',', (string)$this->checkPid_value)
1173
            );
1174
        } else {
1175
            $authInfo['db_user']['checkPidList'] = '';
1176
            $authInfo['db_user']['check_pid_clause'] = '';
1177
        }
1178
        return $authInfo;
1179
    }
1180
1181
    /**
1182
     * DUMMY: Writes to log database table (in some extension classes)
1183
     *
1184
     * @param int $type denotes which module that has submitted the entry. This is the current list:  1=tce_db; 2=tce_file; 3=system (eg. sys_history save); 4=modules; 254=Personal settings changed; 255=login / out action: 1=login, 2=logout, 3=failed login (+ errorcode 3), 4=failure_warning_email sent
1185
     * @param int $action denotes which specific operation that wrote the entry (eg. 'delete', 'upload', 'update' and so on...). Specific for each $type. Also used to trigger update of the interface. (see the log-module for the meaning of each number !!)
1186
     * @param int $error flag. 0 = message, 1 = error (user problem), 2 = System Error (which should not happen), 3 = security notice (admin)
1187
     * @param int $details_nr The message number. Specific for each $type and $action. in the future this will make it possible to translate error messages to other languages
1188
     * @param string $details Default text that follows the message
1189
     * @param array $data Data that follows the log. Might be used to carry special information. If an array the first 5 entries (0-4) will be sprintf'ed the details-text...
1190
     * @param string $tablename Special field used by tce_main.php. These ($tablename, $recuid, $recpid) holds the reference to the record which the log-entry is about. (Was used in attic status.php to update the interface.)
1191
     * @param int|string $recuid Special field used by tce_main.php. These ($tablename, $recuid, $recpid) holds the reference to the record which the log-entry is about. (Was used in attic status.php to update the interface.)
1192
     * @param int|string $recpid Special field used by tce_main.php. These ($tablename, $recuid, $recpid) holds the reference to the record which the log-entry is about. (Was used in attic status.php to update the interface.)
1193
     */
1194
    public function writelog($type, $action, $error, $details_nr, $details, $data, $tablename, $recuid, $recpid)
1195
    {
1196
    }
1197
1198
    /**
1199
     * Raw initialization of the be_user with uid=$uid
1200
     * This will circumvent all login procedures and select a be_users record from the
1201
     * database and set the content of ->user to the record selected.
1202
     * Thus the BE_USER object will appear like if a user was authenticated - however without
1203
     * a session id and the fields from the session table of course.
1204
     * Will check the users for disabled, start/endtime, etc. ($this->user_where_clause())
1205
     *
1206
     * @param int $uid The UID of the backend user to set in ->user
1207
     * @internal
1208
     */
1209
    public function setBeUserByUid($uid)
1210
    {
1211
        $this->user = $this->getRawUserByUid($uid);
1212
    }
1213
1214
    /**
1215
     * Raw initialization of the be_user with username=$name
1216
     *
1217
     * @param string $name The username to look up.
1218
     * @see \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::setBeUserByUid()
1219
     * @internal
1220
     */
1221
    public function setBeUserByName($name)
1222
    {
1223
        $this->user = $this->getRawUserByName($name);
1224
    }
1225
1226
    /**
1227
     * Fetching raw user record with uid=$uid
1228
     *
1229
     * @param int $uid The UID of the backend user to set in ->user
1230
     * @return array user record or FALSE
1231
     * @internal
1232
     */
1233
    public function getRawUserByUid($uid)
1234
    {
1235
        $query = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->user_table);
1236
        $query->setRestrictions($this->userConstraints());
1237
        $query->select('*')
1238
            ->from($this->user_table)
1239
            ->where($query->expr()->eq('uid', $query->createNamedParameter($uid, \PDO::PARAM_INT)));
1240
1241
        return $query->execute()->fetch();
1242
    }
1243
1244
    /**
1245
     * Fetching raw user record with username=$name
1246
     *
1247
     * @param string $name The username to look up.
1248
     * @return array user record or FALSE
1249
     * @see \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::getUserByUid()
1250
     * @internal
1251
     */
1252
    public function getRawUserByName($name)
1253
    {
1254
        $query = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->user_table);
1255
        $query->setRestrictions($this->userConstraints());
1256
        $query->select('*')
1257
            ->from($this->user_table)
1258
            ->where($query->expr()->eq('username', $query->createNamedParameter($name, \PDO::PARAM_STR)));
1259
1260
        return $query->execute()->fetch();
1261
    }
1262
1263
    /**
1264
     * @return UserSession
1265
     */
1266
    public function getSession(): UserSession
1267
    {
1268
        return $this->userSession;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->userSession could return the type null which is incompatible with the type-hinted return TYPO3\CMS\Core\Session\UserSession. Consider adding an additional type-check to rule them out.
Loading history...
1269
    }
1270
1271
    public function __isset(string $propertyName): bool
1272
    {
1273
        switch ($propertyName) {
1274
            case 'id':
1275
                trigger_error('Property id is removed in v11.', E_USER_DEPRECATED);
1276
                return isset($this->userSession);
1277
        }
1278
        return isset($this->propertyName);
0 ignored issues
show
Bug Best Practice introduced by
The property propertyName does not exist on TYPO3\CMS\Core\Authentic...tractUserAuthentication. Since you implemented __get, consider adding a @property annotation.
Loading history...
1279
    }
1280
1281
    public function __set(string $propertyName, $propertyValue)
1282
    {
1283
        switch ($propertyName) {
1284
            case 'id':
1285
                if (!isset($this->userSessionManager)) {
1286
                    $this->initializeUserSessionManager();
1287
                }
1288
                $this->userSession = UserSession::createNonFixated($propertyValue);
1289
                // No deprecation due to adaptions in testing framework to remove ->id = ...
1290
                break;
1291
        }
1292
        $this->$propertyName = $propertyValue;
1293
    }
1294
1295
    public function __get(string $propertyName)
1296
    {
1297
        switch ($propertyName) {
1298
            case 'id':
1299
                trigger_error('Property id is marked as protected now. Use ->getSession()->getIdentifier().', E_USER_DEPRECATED);
1300
                return $this->getSession()->getIdentifier();
1301
        }
1302
        return $this->$propertyName;
1303
    }
1304
1305
    public function __unset(string $propertyName): void
1306
    {
1307
        switch ($propertyName) {
1308
            case 'id':
1309
                trigger_error('Property id is marked as protected now. Use ->getSession()->getIdentifier().', E_USER_DEPRECATED);
1310
                return;
1311
        }
1312
        unset($this->$propertyName);
1313
    }
1314
}
1315