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

AbstractUserAuthentication::writelog()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 0
nc 1
nop 9
dl 0
loc 2
rs 10
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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