Passed
Push — master ( 8cec79...9b5c52 )
by
unknown
15:20
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\Log\LoggerAwareInterface;
19
use Psr\Log\LoggerAwareTrait;
20
use Symfony\Component\HttpFoundation\Cookie;
21
use TYPO3\CMS\Core\Core\Environment;
22
use TYPO3\CMS\Core\Crypto\Random;
23
use TYPO3\CMS\Core\Database\Connection;
24
use TYPO3\CMS\Core\Database\ConnectionPool;
25
use TYPO3\CMS\Core\Database\Query\Restriction\DefaultRestrictionContainer;
26
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
27
use TYPO3\CMS\Core\Database\Query\Restriction\EndTimeRestriction;
28
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
29
use TYPO3\CMS\Core\Database\Query\Restriction\QueryRestrictionContainerInterface;
30
use TYPO3\CMS\Core\Database\Query\Restriction\RootLevelRestriction;
31
use TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction;
32
use TYPO3\CMS\Core\Exception;
33
use TYPO3\CMS\Core\Http\CookieHeaderTrait;
34
use TYPO3\CMS\Core\Session\Backend\Exception\SessionNotFoundException;
35
use TYPO3\CMS\Core\Session\Backend\HashableSessionBackendInterface;
36
use TYPO3\CMS\Core\Session\Backend\SessionBackendInterface;
37
use TYPO3\CMS\Core\Session\SessionManager;
38
use TYPO3\CMS\Core\SysLog\Action\Login as SystemLogLoginAction;
39
use TYPO3\CMS\Core\SysLog\Error as SystemLogErrorClassification;
40
use TYPO3\CMS\Core\SysLog\Type as SystemLogType;
41
use TYPO3\CMS\Core\Utility\GeneralUtility;
42
43
/**
44
 * Authentication of users in TYPO3
45
 *
46
 * This class is used to authenticate a login user.
47
 * The class is used by both the frontend and backend.
48
 * In both cases this class is a parent class to BackendUserAuthentication and FrontendUserAuthentication
49
 *
50
 * See Inside TYPO3 for more information about the API of the class and internal variables.
51
 */
52
abstract class AbstractUserAuthentication implements LoggerAwareInterface
53
{
54
    use LoggerAwareTrait;
55
    use CookieHeaderTrait;
56
57
    /**
58
     * Session/Cookie name
59
     * @var string
60
     */
61
    public $name = '';
62
63
    /**
64
     * Table in database with user data
65
     * @var string
66
     */
67
    public $user_table = '';
68
69
    /**
70
     * Table in database with user groups
71
     * @var string
72
     */
73
    public $usergroup_table = '';
74
75
    /**
76
     * Column for login-name
77
     * @var string
78
     */
79
    public $username_column = '';
80
81
    /**
82
     * Column for password
83
     * @var string
84
     */
85
    public $userident_column = '';
86
87
    /**
88
     * Column for user-id
89
     * @var string
90
     */
91
    public $userid_column = '';
92
93
    /**
94
     * Column for user group information
95
     * @var string
96
     */
97
    public $usergroup_column = '';
98
99
    /**
100
     * Column name for last login timestamp
101
     * @var string
102
     */
103
    public $lastLogin_column = '';
104
105
    /**
106
     * Enable field columns of user table
107
     * @var array
108
     */
109
    public $enablecolumns = [
110
        'rootLevel' => '',
111
        // Boolean: If TRUE, 'AND pid=0' will be a part of the query...
112
        'disabled' => '',
113
        'starttime' => '',
114
        'endtime' => '',
115
        'deleted' => '',
116
    ];
117
118
    /**
119
     * Form field with login-name
120
     * @var string
121
     */
122
    public $formfield_uname = '';
123
124
    /**
125
     * Form field with password
126
     * @var string
127
     */
128
    public $formfield_uident = '';
129
130
    /**
131
     * Form field with status: *'login', 'logout'. If empty login is not verified.
132
     * @var string
133
     */
134
    public $formfield_status = '';
135
136
    /**
137
     * Session timeout (on the server)
138
     *
139
     * If >0: session-timeout in seconds.
140
     * If <=0: Instant logout after login.
141
     *
142
     * @var int
143
     */
144
    public $sessionTimeout = 0;
145
146
    /**
147
     * Lifetime for the session-cookie (on the client)
148
     *
149
     * If >0: permanent cookie with given lifetime
150
     * If 0: session-cookie
151
     * Session-cookie means the browser will remove it when the browser is closed.
152
     *
153
     * @var int
154
     */
155
    public $lifetime = 0;
156
157
    /**
158
     * GarbageCollection
159
     * Purge all server session data older than $gc_time seconds.
160
     * if $this->sessionTimeout > 0, then the session timeout is used instead.
161
     * @var int
162
     */
163
    public $gc_time = 86400;
164
165
    /**
166
     * Probability for garbage collection to be run (in percent)
167
     * @var int
168
     */
169
    public $gc_probability = 1;
170
171
    /**
172
     * Decides if the writelog() function is called at login and logout
173
     * @var bool
174
     */
175
    public $writeStdLog = false;
176
177
    /**
178
     * Log failed login attempts
179
     * @var bool
180
     */
181
    public $writeAttemptLog = false;
182
183
    /**
184
     * Send no-cache headers
185
     * @var bool
186
     */
187
    public $sendNoCacheHeaders = true;
188
189
    /**
190
     * The ident-hash is normally 32 characters and should be!
191
     * But if you are making sites for WAP-devices or other low-bandwidth stuff,
192
     * you may shorten the length.
193
     * Never let this value drop below 6!
194
     * A length of 6 would give you more than 16 mio possibilities.
195
     * @var int
196
     */
197
    public $hash_length = 32;
198
199
    /**
200
     * If set, the user-record must be stored at the page defined by $checkPid_value
201
     * @var bool
202
     */
203
    public $checkPid = true;
204
205
    /**
206
     * The page id the user record must be stored at
207
     * @var int
208
     */
209
    public $checkPid_value = 0;
210
211
    /**
212
     * session_id (MD5-hash)
213
     * @var string
214
     * @internal
215
     */
216
    public $id;
217
218
    /**
219
     * Will be set to TRUE if the login session is actually written during auth-check.
220
     * @var bool
221
     */
222
    public $loginSessionStarted = false;
223
224
    /**
225
     * @var array|null contains user- AND session-data from database (joined tables)
226
     * @internal
227
     */
228
    public $user;
229
230
    /**
231
     * Will be set to TRUE if a new session ID was created
232
     * @var bool
233
     */
234
    public $newSessionID = false;
235
236
    /**
237
     * Will force the session cookie to be set every time (lifetime must be 0)
238
     * @var bool
239
     */
240
    public $forceSetCookie = false;
241
242
    /**
243
     * Will prevent the setting of the session cookie (takes precedence over forceSetCookie)
244
     * @var bool
245
     */
246
    public $dontSetCookie = false;
247
248
    /**
249
     * @var bool
250
     */
251
    protected $cookieWasSetOnCurrentRequest = false;
252
253
    /**
254
     * Login type, used for services.
255
     * @var string
256
     */
257
    public $loginType = '';
258
259
    /**
260
     * @var array
261
     */
262
    public $uc;
263
264
    /**
265
     * @var IpLocker
266
     */
267
    protected $ipLocker;
268
269
    /**
270
     * @var SessionBackendInterface
271
     */
272
    protected $sessionBackend;
273
274
    /**
275
     * Holds deserialized data from session records.
276
     * 'Reserved' keys are:
277
     *   - 'sys': Reserved for TypoScript standard code.
278
     * @var array
279
     */
280
    protected $sessionData = [];
281
282
    /**
283
     * Initialize some important variables
284
     *
285
     * @throws Exception
286
     */
287
    public function __construct()
288
    {
289
        // If there is a custom session timeout, use this instead of the 1d default gc time.
290
        if ($this->sessionTimeout > 0) {
291
            $this->gc_time = $this->sessionTimeout;
292
        }
293
        // Backend or frontend login - used for auth services
294
        if (empty($this->loginType)) {
295
            throw new Exception('No loginType defined, must be set explicitly by subclass', 1476045345);
296
        }
297
298
        $this->ipLocker = GeneralUtility::makeInstance(
299
            IpLocker::class,
300
            $GLOBALS['TYPO3_CONF_VARS'][$this->loginType]['lockIP'],
301
            $GLOBALS['TYPO3_CONF_VARS'][$this->loginType]['lockIPv6']
302
        );
303
    }
304
305
    /**
306
     * Starts a user session
307
     * Typical configurations will:
308
     * a) check if session cookie was set and if not, set one,
309
     * b) check if a password/username was sent and if so, try to authenticate the user
310
     * c) Lookup a session attached to a user and check timeout etc.
311
     * d) Garbage collection, setting of no-cache headers.
312
     * If a user is authenticated the database record of the user (array) will be set in the ->user internal variable.
313
     */
314
    public function start()
315
    {
316
        $this->logger->debug('## Beginning of auth logging.');
317
        $this->newSessionID = false;
318
        // Make certain that NO user is set initially
319
        $this->user = null;
320
        // sessionID is set to ses_id if cookie is present. Otherwise a new session will start
321
        $this->id = $this->getCookie($this->name);
322
323
        // If new session or client tries to fix session...
324
        if (!$this->isExistingSessionRecord($this->id)) {
325
            $this->id = $this->createSessionId();
326
            $this->newSessionID = true;
327
        }
328
329
        // Load user session, check to see if anyone has submitted login-information and if so authenticate
330
        // the user with the session. $this->user[uid] may be used to write log...
331
        $this->checkAuthentication();
332
        // Set cookie if generally enabled or if the current session is a non-session cookie (FE permalogin)
333
        if (!$this->dontSetCookie || $this->isRefreshTimeBasedCookie()) {
334
            $this->setSessionCookie();
335
        }
336
        // Hook for alternative ways of filling the $this->user array (is used by the "timtaw" extension)
337
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['postUserLookUp'] ?? [] as $funcName) {
338
            $_params = [
339
                'pObj' => $this,
340
            ];
341
            GeneralUtility::callUserFunction($funcName, $_params, $this);
342
        }
343
        // If we're lucky we'll get to clean up old sessions
344
        if (random_int(0, mt_getrandmax()) % 100 <= $this->gc_probability) {
345
            $this->gc();
0 ignored issues
show
Bug introduced by
The method gc() 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

345
            $this->/** @scrutinizer ignore-call */ 
346
                   gc();

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...
346
        }
347
    }
348
349
    /**
350
     * Sets the session cookie for the current disposal.
351
     *
352
     * @throws Exception
353
     */
354
    protected function setSessionCookie()
355
    {
356
        $isSetSessionCookie = $this->isSetSessionCookie();
357
        $isRefreshTimeBasedCookie = $this->isRefreshTimeBasedCookie();
358
        if ($isSetSessionCookie || $isRefreshTimeBasedCookie) {
359
            // Get the domain to be used for the cookie (if any):
360
            $cookieDomain = $this->getCookieDomain();
361
            // If no cookie domain is set, use the base path:
362
            $cookiePath = $cookieDomain ? '/' : GeneralUtility::getIndpEnv('TYPO3_SITE_PATH');
363
            // If the cookie lifetime is set, use it:
364
            $cookieExpire = $isRefreshTimeBasedCookie ? $GLOBALS['EXEC_TIME'] + $this->lifetime : 0;
365
            // Valid options are "strict", "lax" or "none", whereas "none" only works in HTTPS requests (default & fallback is "strict")
366
            $cookieSameSite = $this->sanitizeSameSiteCookieValue(
367
                strtolower($GLOBALS['TYPO3_CONF_VARS'][$this->loginType]['cookieSameSite'] ?? Cookie::SAMESITE_STRICT)
368
            );
369
            // Use the secure option when the current request is served by a secure connection:
370
            // SameSite "none" needs the secure option (only allowed on HTTPS)
371
            $isSecure = $cookieSameSite === Cookie::SAMESITE_NONE || GeneralUtility::getIndpEnv('TYPO3_SSL');
372
            $cookie = new Cookie(
373
                $this->name,
374
                $this->id,
375
                $cookieExpire,
376
                $cookiePath,
377
                $cookieDomain,
378
                $isSecure,
379
                true,
380
                false,
381
                $cookieSameSite
382
            );
383
            header('Set-Cookie: ' . $cookie->__toString(), false);
384
            $this->cookieWasSetOnCurrentRequest = true;
385
            $this->logger->debug(
386
                ($isRefreshTimeBasedCookie ? 'Updated Cookie: ' : 'Set Cookie: ')
387
                . $this->id . ($cookieDomain ? ', ' . $cookieDomain : '')
388
            );
389
        }
390
    }
391
392
    /**
393
     * Gets the domain to be used on setting cookies.
394
     * The information is taken from the value in $GLOBALS['TYPO3_CONF_VARS']['SYS']['cookieDomain'].
395
     *
396
     * @return string The domain to be used on setting cookies
397
     */
398
    protected function getCookieDomain()
399
    {
400
        $result = '';
401
        $cookieDomain = $GLOBALS['TYPO3_CONF_VARS']['SYS']['cookieDomain'];
402
        // If a specific cookie domain is defined for a given application type, use that domain
403
        if (!empty($GLOBALS['TYPO3_CONF_VARS'][$this->loginType]['cookieDomain'])) {
404
            $cookieDomain = $GLOBALS['TYPO3_CONF_VARS'][$this->loginType]['cookieDomain'];
405
        }
406
        if ($cookieDomain) {
407
            if ($cookieDomain[0] === '/') {
408
                $match = [];
409
                $matchCnt = @preg_match($cookieDomain, GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY'), $match);
410
                if ($matchCnt === false) {
411
                    $this->logger->critical('The regular expression for the cookie domain (' . $cookieDomain . ') contains errors. The session is not shared across sub-domains.');
412
                } elseif ($matchCnt) {
413
                    $result = $match[0];
414
                }
415
            } else {
416
                $result = $cookieDomain;
417
            }
418
        }
419
        return $result;
420
    }
421
422
    /**
423
     * Get the value of a specified cookie.
424
     *
425
     * @param string $cookieName The cookie ID
426
     * @return string The value stored in the cookie
427
     */
428
    protected function getCookie($cookieName)
429
    {
430
        return isset($_COOKIE[$cookieName]) ? stripslashes($_COOKIE[$cookieName]) : '';
431
    }
432
433
    /**
434
     * Determine whether a session cookie needs to be set (lifetime=0)
435
     *
436
     * @return bool
437
     * @internal
438
     */
439
    public function isSetSessionCookie()
440
    {
441
        return ($this->newSessionID || $this->forceSetCookie) && $this->lifetime == 0;
442
    }
443
444
    /**
445
     * Determine whether a non-session cookie needs to be set (lifetime>0)
446
     *
447
     * @return bool
448
     * @internal
449
     */
450
    public function isRefreshTimeBasedCookie()
451
    {
452
        return $this->lifetime > 0;
453
    }
454
455
    /**
456
     * "auth" services configuration array from $GLOBALS['TYPO3_CONF_VARS']['SVCONF']['auth']
457
     * @return array
458
     */
459
    protected function getAuthServiceConfiguration(): array
460
    {
461
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['SVCONF']['auth']['setup'] ?? null)) {
462
            return $GLOBALS['TYPO3_CONF_VARS']['SVCONF']['auth']['setup'];
463
        }
464
        return [];
465
    }
466
467
    /**
468
     * Checks if a submission of username and password is present or use other authentication by auth services
469
     *
470
     * @throws \RuntimeException
471
     * @internal
472
     */
473
    public function checkAuthentication()
474
    {
475
        $authConfiguration = $this->getAuthServiceConfiguration();
476
        if (!empty($authConfiguration)) {
477
            $this->logger->debug('Authentication Service Configuration found.', $authConfiguration);
478
        }
479
        // No user for now - will be searched by service below
480
        $tempuserArr = [];
481
        $tempuser = false;
482
        // User is not authenticated by default
483
        $authenticated = false;
484
        // User want to login with passed login data (name/password)
485
        $activeLogin = false;
486
        $this->logger->debug('Login type: ' . $this->loginType);
487
        // The info array provide additional information for auth services
488
        $authInfo = $this->getAuthInfoArray();
489
        // Get Login/Logout data submitted by a form or params
490
        $loginData = $this->getLoginFormData();
491
        $this->logger->debug('Login data', $loginData);
492
        // Active logout (eg. with "logout" button)
493
        if ($loginData['status'] === LoginType::LOGOUT) {
494
            if ($this->writeStdLog) {
495
                // $type,$action,$error,$details_nr,$details,$data,$tablename,$recuid,$recpid
496
                $this->writelog(SystemLogType::LOGIN, SystemLogLoginAction::LOGOUT, SystemLogErrorClassification::MESSAGE, 2, 'User %s logged out', [$this->user['username']], '', 0, 0);
497
            }
498
            $this->logger->info('User logged out. Id: ' . $this->id);
499
            $this->logoff();
500
        }
501
        // Determine whether we need to skip session update.
502
        // This is used mainly for checking session timeout in advance without refreshing the current session's timeout.
503
        $skipSessionUpdate = (bool)GeneralUtility::_GP('skipSessionUpdate');
504
        $haveSession = false;
505
        $anonymousSession = false;
506
        if (!$this->newSessionID) {
507
            // Read user session
508
            $authInfo['userSession'] = $this->fetchUserSession($skipSessionUpdate);
509
            $haveSession = is_array($authInfo['userSession']);
510
            if ($haveSession && !empty($authInfo['userSession']['ses_anonymous'])) {
511
                $anonymousSession = true;
512
            }
513
        }
514
515
        // Active login (eg. with login form).
516
        if (!$haveSession && $loginData['status'] === LoginType::LOGIN) {
517
            $activeLogin = true;
518
            $this->logger->debug('Active login (eg. with login form)');
519
            // check referrer for submitted login values
520
            if ($this->formfield_status && $loginData['uident'] && $loginData['uname']) {
521
                // Delete old user session if any
522
                $this->logoff();
523
            }
524
            // Refuse login for _CLI users, if not processing a CLI request type
525
            // (although we shouldn't be here in case of a CLI request type)
526
            if (stripos($loginData['uname'], '_CLI_') === 0 && !Environment::isCli()) {
527
                throw new \RuntimeException('TYPO3 Fatal Error: You have tried to login using a CLI user. Access prohibited!', 1270853931);
528
            }
529
        }
530
531
        // Cause elevation of privilege, make sure regenerateSessionId is called later on
532
        if ($anonymousSession && $loginData['status'] === LoginType::LOGIN) {
533
            $activeLogin = true;
534
        }
535
536
        if ($haveSession) {
537
            $this->logger->debug('User session found', [
538
                $this->userid_column => $authInfo['userSession'][$this->userid_column] ?? null,
539
                $this->username_column => $authInfo['userSession'][$this->username_column] ?? null,
540
            ]);
541
        } else {
542
            $this->logger->debug('No user session found');
543
        }
544
545
        // Fetch user if ...
546
        if (
547
            $activeLogin || !empty($authConfiguration[$this->loginType . '_alwaysFetchUser'])
548
            || !$haveSession && !empty($authConfiguration[$this->loginType . '_fetchUserIfNoSession'])
549
        ) {
550
            // Use 'auth' service to find the user
551
            // First found user will be used
552
            $subType = 'getUser' . $this->loginType;
553
            /** @var AuthenticationService $serviceObj */
554
            foreach ($this->getAuthServices($subType, $loginData, $authInfo) as $serviceObj) {
555
                if ($row = $serviceObj->getUser()) {
556
                    $tempuserArr[] = $row;
557
                    $this->logger->debug('User found', [
558
                        $this->userid_column => $row[$this->userid_column],
559
                        $this->username_column => $row[$this->username_column],
560
                    ]);
561
                    // User found, just stop to search for more if not configured to go on
562
                    if (empty($authConfiguration[$this->loginType . '_fetchAllUsers'])) {
563
                        break;
564
                    }
565
                }
566
            }
567
568
            if (!empty($authConfiguration[$this->loginType . '_alwaysFetchUser'])) {
569
                $this->logger->debug($this->loginType . '_alwaysFetchUser option is enabled');
570
            }
571
            if (empty($tempuserArr)) {
572
                $this->logger->debug('No user found by services');
573
            } else {
574
                $this->logger->debug(count($tempuserArr) . ' user records found by services');
575
            }
576
        }
577
578
        // If no new user was set we use the already found user session
579
        if (empty($tempuserArr) && $haveSession && !$anonymousSession) {
580
            $tempuserArr[] = $authInfo['userSession'];
581
            $tempuser = $authInfo['userSession'];
582
            // User is authenticated because we found a user session
583
            $authenticated = true;
584
            $this->logger->debug('User session used', [
585
                $this->userid_column => $authInfo['userSession'][$this->userid_column],
586
                $this->username_column => $authInfo['userSession'][$this->username_column],
587
            ]);
588
        }
589
        // Re-auth user when 'auth'-service option is set
590
        if (!empty($authConfiguration[$this->loginType . '_alwaysAuthUser'])) {
591
            $authenticated = false;
592
            $this->logger->debug('alwaysAuthUser option is enabled');
593
        }
594
        // Authenticate the user if needed
595
        if (!empty($tempuserArr) && !$authenticated) {
596
            foreach ($tempuserArr as $tempuser) {
597
                // Use 'auth' service to authenticate the user
598
                // If one service returns FALSE then authentication failed
599
                // a service might return 100 which means there's no reason to stop but the user can't be authenticated by that service
600
                $this->logger->debug('Auth user', $tempuser);
601
                $subType = 'authUser' . $this->loginType;
602
603
                /** @var AuthenticationService $serviceObj */
604
                foreach ($this->getAuthServices($subType, $loginData, $authInfo) as $serviceObj) {
605
                    if (($ret = $serviceObj->authUser($tempuser)) > 0) {
606
                        // If the service returns >=200 then no more checking is needed - useful for IP checking without password
607
                        if ((int)$ret >= 200) {
608
                            $authenticated = true;
609
                            break;
610
                        }
611
                        if ((int)$ret >= 100) {
612
                        } else {
613
                            $authenticated = true;
614
                        }
615
                    } else {
616
                        $authenticated = false;
617
                        break;
618
                    }
619
                }
620
621
                if ($authenticated) {
622
                    // Leave foreach() because a user is authenticated
623
                    break;
624
                }
625
            }
626
        }
627
628
        // If user is authenticated a valid user is in $tempuser
629
        if ($authenticated) {
630
            // Insert session record if needed:
631
            if (!$haveSession || $anonymousSession || $tempuser['ses_id'] != $this->id && $tempuser['uid'] != $authInfo['userSession']['ses_userid']) {
632
                $sessionData = $this->createUserSession($tempuser);
633
634
                // Preserve session data on login
635
                if ($anonymousSession) {
636
                    $sessionData = $this->getSessionBackend()->update(
637
                        $this->id,
638
                        ['ses_data' => $authInfo['userSession']['ses_data']]
639
                    );
640
                }
641
642
                $this->user = array_merge(
643
                    $tempuser,
644
                    $sessionData
645
                );
646
                // The login session is started.
647
                $this->loginSessionStarted = true;
648
                if (is_array($this->user)) {
0 ignored issues
show
introduced by
The condition is_array($this->user) is always true.
Loading history...
649
                    $this->logger->debug('User session finally read', [
650
                        $this->userid_column => $this->user[$this->userid_column],
651
                        $this->username_column => $this->user[$this->username_column],
652
                    ]);
653
                }
654
            } elseif ($haveSession) {
0 ignored issues
show
introduced by
The condition $haveSession is always true.
Loading history...
655
                // Validate the session ID and promote it
656
                // This check can be removed in TYPO3 v11
657
                if ($this->getSessionBackend() instanceof HashableSessionBackendInterface && !empty($authInfo['userSession']['ses_id'] ?? '')) {
658
                    // The session is stored in plaintext, promote it
659
                    if ($authInfo['userSession']['ses_id'] === $this->id) {
660
                        $authInfo['userSession'] = $this->getSessionBackend()->update(
661
                            $this->id,
662
                            ['ses_data' => $authInfo['userSession']['ses_data']]
663
                        );
664
                    }
665
                }
666
                // if we come here the current session is for sure not anonymous as this is a pre-condition for $authenticated = true
667
                $this->user = $authInfo['userSession'];
668
            }
669
670
            if ($activeLogin && !$this->newSessionID) {
671
                $this->regenerateSessionId();
672
            }
673
674
            if ($activeLogin) {
675
                // User logged in - write that to the log!
676
                if ($this->writeStdLog) {
677
                    $this->writelog(SystemLogType::LOGIN, SystemLogLoginAction::LOGIN, SystemLogErrorClassification::MESSAGE, 1, 'User %s logged in from ###IP###', [$tempuser[$this->username_column]], '', '', '');
678
                }
679
                $this->logger->info('User ' . $tempuser[$this->username_column] . ' logged in from ' . GeneralUtility::getIndpEnv('REMOTE_ADDR'));
680
            } else {
681
                $this->logger->debug('User ' . $tempuser[$this->username_column] . ' authenticated from ' . GeneralUtility::getIndpEnv('REMOTE_ADDR'));
682
            }
683
        } else {
684
            // User was not authenticated, so we should reuse the existing anonymous session
685
            if ($anonymousSession) {
686
                $this->user = $authInfo['userSession'];
687
            }
688
689
            // Mark the current login attempt as failed
690
            if (empty($tempuserArr) && $activeLogin) {
691
                $this->logger->debug('Login failed', [
692
                    'loginData' => $loginData
693
                ]);
694
            } elseif (!empty($tempuserArr)) {
695
                $this->logger->debug('Login failed', [
696
                    $this->userid_column => $tempuser[$this->userid_column],
697
                    $this->username_column => $tempuser[$this->username_column],
698
                ]);
699
            }
700
701
            // If there were a login failure, check to see if a warning email should be sent
702
            if ($activeLogin) {
703
                $this->handleLoginFailure();
704
            }
705
        }
706
    }
707
708
    /**
709
     * Implement functionality when there was a failed login
710
     */
711
    protected function handleLoginFailure(): void
712
    {
713
        $_params = [];
714
        $sleep = true;
715
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['postLoginFailureProcessing'] ?? [] as $hookIdentifier => $_funcRef) {
716
            GeneralUtility::callUserFunction($_funcRef, $_params, $this);
717
            // This hack will be removed once this is migrated into PSR-14 Events
718
            if ($hookIdentifier !== 'sendEmailOnFailedLoginAttempt') {
719
                $sleep = false;
720
            }
721
        }
722
        if ($sleep) {
723
            // No hooks were triggered - default login failure behavior is to sleep 5 seconds
724
            sleep(5);
725
        }
726
    }
727
728
    /**
729
     * Creates a new session ID.
730
     *
731
     * @return string The new session ID
732
     */
733
    public function createSessionId()
734
    {
735
        return GeneralUtility::makeInstance(Random::class)->generateRandomHexString($this->hash_length);
736
    }
737
738
    /**
739
     * Initializes authentication services to be used in a foreach loop
740
     *
741
     * @param string $subType e.g. getUserFE
742
     * @param array $loginData
743
     * @param array $authInfo
744
     * @return \Traversable A generator of service objects
745
     */
746
    protected function getAuthServices(string $subType, array $loginData, array $authInfo): \Traversable
747
    {
748
        $serviceChain = [];
749
        while (is_object($serviceObj = GeneralUtility::makeInstanceService('auth', $subType, $serviceChain))) {
750
            $serviceChain[] = $serviceObj->getServiceKey();
751
            $serviceObj->initAuth($subType, $loginData, $authInfo, $this);
752
            yield $serviceObj;
753
        }
754
        if (!empty($serviceChain)) {
755
            $this->logger->debug($subType . ' auth services called: ' . implode(', ', $serviceChain));
756
        }
757
    }
758
759
    /**
760
     * Regenerate the session ID and transfer the session to new ID
761
     * Call this method whenever a user proceeds to a higher authorization level
762
     * e.g. when an anonymous session is now authenticated.
763
     *
764
     * @param array $existingSessionRecord If given, this session record will be used instead of fetching again
765
     * @param bool $anonymous If true session will be regenerated as anonymous session
766
     */
767
    protected function regenerateSessionId(array $existingSessionRecord = [], bool $anonymous = false)
768
    {
769
        if (empty($existingSessionRecord)) {
770
            $existingSessionRecord = $this->getSessionBackend()->get($this->id);
771
        }
772
773
        // Update session record with new ID
774
        $oldSessionId = $this->id;
775
        $this->id = $this->createSessionId();
776
        $updatedSession = $this->getSessionBackend()->set($this->id, $existingSessionRecord);
777
        $this->sessionData = unserialize($updatedSession['ses_data']);
778
        // Merge new session data into user/session array
779
        $this->user = array_merge($this->user ?? [], $updatedSession);
780
        $this->getSessionBackend()->remove($oldSessionId);
781
        $this->newSessionID = true;
782
    }
783
784
    /*************************
785
     *
786
     * User Sessions
787
     *
788
     *************************/
789
790
    /**
791
     * Creates a user session record and returns its values.
792
     *
793
     * @param array $tempuser User data array
794
     *
795
     * @return array The session data for the newly created session.
796
     */
797
    public function createUserSession($tempuser)
798
    {
799
        $this->logger->debug('Create session ses_id = ' . $this->id);
800
        // Delete any session entry first
801
        $this->getSessionBackend()->remove($this->id);
802
        // Re-create session entry
803
        $sessionRecord = $this->getNewSessionRecord($tempuser);
804
        $sessionRecord = $this->getSessionBackend()->set($this->id, $sessionRecord);
805
        // Updating lastLogin_column carrying information about last login.
806
        $this->updateLoginTimestamp($tempuser[$this->userid_column]);
807
        return $sessionRecord;
808
    }
809
810
    /**
811
     * Updates the last login column in the user with the given id
812
     *
813
     * @param int $userId
814
     */
815
    protected function updateLoginTimestamp(int $userId)
816
    {
817
        if ($this->lastLogin_column) {
818
            $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->user_table);
819
            $connection->update(
820
                $this->user_table,
821
                [$this->lastLogin_column => $GLOBALS['EXEC_TIME']],
822
                [$this->userid_column => $userId]
823
            );
824
        }
825
    }
826
827
    /**
828
     * Returns a new session record for the current user for insertion into the DB.
829
     * This function is mainly there as a wrapper for inheriting classes to override it.
830
     *
831
     * @param array $tempuser
832
     * @return array User session record
833
     */
834
    public function getNewSessionRecord($tempuser)
835
    {
836
        $sessionIpLock = $this->ipLocker->getSessionIpLock((string)GeneralUtility::getIndpEnv('REMOTE_ADDR'));
837
838
        return [
839
            'ses_id' => $this->id,
840
            'ses_iplock' => $sessionIpLock,
841
            'ses_userid' => $tempuser[$this->userid_column] ?? 0,
842
            'ses_tstamp' => $GLOBALS['EXEC_TIME'],
843
            'ses_data' => '',
844
        ];
845
    }
846
847
    /**
848
     * Read the user session from db.
849
     *
850
     * @param bool $skipSessionUpdate
851
     * @return array|bool User session data, false if $this->id does not represent valid session
852
     */
853
    public function fetchUserSession($skipSessionUpdate = false)
854
    {
855
        $this->logger->debug('Fetch session ses_id = ' . $this->id);
856
        try {
857
            $sessionRecord = $this->getSessionBackend()->get($this->id);
858
        } catch (SessionNotFoundException $e) {
859
            return false;
860
        }
861
862
        $this->sessionData = unserialize($sessionRecord['ses_data']);
863
        // Session is anonymous so no need to fetch user
864
        if (!empty($sessionRecord['ses_anonymous'])) {
865
            return $sessionRecord;
866
        }
867
868
        // Fetch the user from the DB
869
        $userRecord = $this->getRawUserByUid((int)$sessionRecord['ses_userid']);
870
        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...
871
            $userRecord = array_merge($sessionRecord, $userRecord);
872
            // A user was found
873
            $userRecord['ses_tstamp'] = (int)$userRecord['ses_tstamp'];
874
            $userRecord['is_online'] = (int)$userRecord['ses_tstamp'];
875
876
            // If sessionTimeout > 0 (TRUE) and current time has not exceeded the latest sessions-time plus the timeout in seconds then accept user
877
            // Use a gracetime-value to avoid updating a session-record too often
878
            if ($this->sessionTimeout > 0 && $GLOBALS['EXEC_TIME'] < $userRecord['ses_tstamp'] + $this->sessionTimeout) {
879
                $sessionUpdateGracePeriod = 61;
880
                if (!$skipSessionUpdate && $GLOBALS['EXEC_TIME'] > ($userRecord['ses_tstamp'] + $sessionUpdateGracePeriod)) {
881
                    // Update the session timestamp by writing a dummy update. (Backend will update the timestamp)
882
                    $updatesSession = $this->getSessionBackend()->update($this->id, []);
883
                    $userRecord = array_merge($userRecord, $updatesSession);
884
                }
885
            } else {
886
                // Delete any user set...
887
                $this->logoff();
888
                $userRecord = false;
889
            }
890
        }
891
        return $userRecord;
892
    }
893
894
    /**
895
     * Regenerates the session ID and sets the cookie again.
896
     *
897
     * @internal
898
     */
899
    public function enforceNewSessionId()
900
    {
901
        $this->regenerateSessionId();
902
        $this->setSessionCookie();
903
    }
904
905
    /**
906
     * Log out current user!
907
     * Removes the current session record, sets the internal ->user array to a blank string;
908
     * Thereby the current user (if any) is effectively logged out!
909
     */
910
    public function logoff()
911
    {
912
        $this->logger->debug('logoff: ses_id = ' . $this->id);
913
914
        $_params = [];
915
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'] ?? [] as $_funcRef) {
916
            if ($_funcRef) {
917
                GeneralUtility::callUserFunction($_funcRef, $_params, $this);
918
            }
919
        }
920
        $this->performLogoff();
921
922
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_post_processing'] ?? [] as $_funcRef) {
923
            if ($_funcRef) {
924
                GeneralUtility::callUserFunction($_funcRef, $_params, $this);
925
            }
926
        }
927
    }
928
929
    /**
930
     * Perform the logoff action. Called from logoff() as a way to allow subclasses to override
931
     * what happens when a user logs off, without needing to reproduce the hook calls and logging
932
     * that happens in the public logoff() API method.
933
     */
934
    protected function performLogoff()
935
    {
936
        if ($this->id) {
937
            $this->getSessionBackend()->remove($this->id);
938
        }
939
        $this->sessionData = [];
940
        $this->user = null;
941
        if ($this->isCookieSet()) {
942
            $this->removeCookie($this->name);
943
        }
944
    }
945
946
    /**
947
     * Empty / unset the cookie
948
     *
949
     * @param string $cookieName usually, this is $this->name
950
     */
951
    public function removeCookie($cookieName)
952
    {
953
        $cookieDomain = $this->getCookieDomain();
954
        // If no cookie domain is set, use the base path
955
        $cookiePath = $cookieDomain ? '/' : GeneralUtility::getIndpEnv('TYPO3_SITE_PATH');
956
        setcookie($cookieName, '', -1, $cookiePath, $cookieDomain);
957
    }
958
959
    /**
960
     * Determine whether there's an according session record to a given session_id.
961
     * Don't care if session record is still valid or not.
962
     *
963
     * @param string $id Claimed Session ID
964
     * @return bool Returns TRUE if a corresponding session was found in the database
965
     */
966
    public function isExistingSessionRecord($id)
967
    {
968
        if (empty($id)) {
969
            return false;
970
        }
971
        try {
972
            $sessionRecord = $this->getSessionBackend()->get($id);
973
            if (empty($sessionRecord)) {
974
                return false;
975
            }
976
            // If the session does not match the current IP lock, it should be treated as invalid
977
            // and a new session should be created.
978
            return $this->ipLocker->validateRemoteAddressAgainstSessionIpLock((string)GeneralUtility::getIndpEnv('REMOTE_ADDR'), $sessionRecord['ses_iplock']);
979
        } catch (SessionNotFoundException $e) {
980
            return false;
981
        }
982
    }
983
984
    /**
985
     * Returns whether this request is going to set a cookie
986
     * or a cookie was already found in the system
987
     *
988
     * @return bool Returns TRUE if a cookie is set
989
     */
990
    public function isCookieSet()
991
    {
992
        return $this->cookieWasSetOnCurrentRequest || $this->getCookie($this->name);
993
    }
994
995
    /*************************
996
     *
997
     * SQL Functions
998
     *
999
     *************************/
1000
    /**
1001
     * This returns the restrictions needed to select the user respecting
1002
     * enable columns and flags like deleted, hidden, starttime, endtime
1003
     * and rootLevel
1004
     *
1005
     * @return QueryRestrictionContainerInterface
1006
     * @internal
1007
     */
1008
    protected function userConstraints(): QueryRestrictionContainerInterface
1009
    {
1010
        $restrictionContainer = GeneralUtility::makeInstance(DefaultRestrictionContainer::class);
1011
1012
        if (empty($this->enablecolumns['disabled'])) {
1013
            $restrictionContainer->removeByType(HiddenRestriction::class);
1014
        }
1015
1016
        if (empty($this->enablecolumns['deleted'])) {
1017
            $restrictionContainer->removeByType(DeletedRestriction::class);
1018
        }
1019
1020
        if (empty($this->enablecolumns['starttime'])) {
1021
            $restrictionContainer->removeByType(StartTimeRestriction::class);
1022
        }
1023
1024
        if (empty($this->enablecolumns['endtime'])) {
1025
            $restrictionContainer->removeByType(EndTimeRestriction::class);
1026
        }
1027
1028
        if (!empty($this->enablecolumns['rootLevel'])) {
1029
            $restrictionContainer->add(GeneralUtility::makeInstance(RootLevelRestriction::class, [$this->user_table]));
1030
        }
1031
1032
        return $restrictionContainer;
1033
    }
1034
1035
    /*************************
1036
     *
1037
     * Session and Configuration Handling
1038
     *
1039
     *************************/
1040
    /**
1041
     * This writes $variable to the user-record. This is a way of providing session-data.
1042
     * You can fetch the data again through $this->uc in this class!
1043
     * If $variable is not an array, $this->uc is saved!
1044
     *
1045
     * @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
1046
     */
1047
    public function writeUC($variable = '')
1048
    {
1049
        if (is_array($this->user) && $this->user[$this->userid_column]) {
1050
            if (!is_array($variable)) {
1051
                $variable = $this->uc;
1052
            }
1053
            $this->logger->debug('writeUC: ' . $this->userid_column . '=' . (int)$this->user[$this->userid_column]);
1054
            GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->user_table)->update(
1055
                $this->user_table,
1056
                ['uc' => serialize($variable)],
1057
                [$this->userid_column => (int)$this->user[$this->userid_column]],
1058
                ['uc' => Connection::PARAM_LOB]
1059
            );
1060
        }
1061
    }
1062
1063
    /**
1064
     * Sets $theUC as the internal variable ->uc IF $theUC is an array.
1065
     * If $theUC is FALSE, the 'uc' content from the ->user array will be unserialized and restored in ->uc
1066
     *
1067
     * @param mixed $theUC If an array, then set as ->uc, otherwise load from user record
1068
     */
1069
    public function unpack_uc($theUC = '')
1070
    {
1071
        if (!$theUC && isset($this->user['uc'])) {
1072
            $theUC = unserialize($this->user['uc'], ['allowed_classes' => false]);
1073
        }
1074
        if (is_array($theUC)) {
1075
            $this->uc = $theUC;
1076
        }
1077
    }
1078
1079
    /**
1080
     * Stores data for a module.
1081
     * The data is stored with the session id so you can even check upon retrieval
1082
     * if the module data is from a previous session or from the current session.
1083
     *
1084
     * @param string $module Is the name of the module ($MCONF['name'])
1085
     * @param mixed $data Is the data you want to store for that module (array, string, ...)
1086
     * @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.
1087
     */
1088
    public function pushModuleData($module, $data, $noSave = 0)
1089
    {
1090
        $this->uc['moduleData'][$module] = $data;
1091
        $this->uc['moduleSessionID'][$module] = $this->id;
1092
        if (!$noSave) {
1093
            $this->writeUC();
1094
        }
1095
    }
1096
1097
    /**
1098
     * Gets module data for a module (from a loaded ->uc array)
1099
     *
1100
     * @param string $module Is the name of the module ($MCONF['name'])
1101
     * @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).
1102
     * @return mixed The module data if available: $this->uc['moduleData'][$module];
1103
     */
1104
    public function getModuleData($module, $type = '')
1105
    {
1106
        if ($type !== 'ses' || (isset($this->uc['moduleSessionID'][$module]) && $this->uc['moduleSessionID'][$module] == $this->id)) {
1107
            return $this->uc['moduleData'][$module];
1108
        }
1109
        return null;
1110
    }
1111
1112
    /**
1113
     * Returns the session data stored for $key.
1114
     * The data will last only for this login session since it is stored in the user session.
1115
     *
1116
     * @param string $key The key associated with the session data
1117
     * @return mixed
1118
     */
1119
    public function getSessionData($key)
1120
    {
1121
        return $this->sessionData[$key] ?? null;
1122
    }
1123
1124
    /**
1125
     * Set session data by key.
1126
     * The data will last only for this login session since it is stored in the user session.
1127
     *
1128
     * @param string $key A non empty string to store the data under
1129
     * @param mixed $data Data store store in session
1130
     */
1131
    public function setSessionData($key, $data)
1132
    {
1133
        if (empty($key)) {
1134
            throw new \InvalidArgumentException('Argument key must not be empty', 1484311516);
1135
        }
1136
        $this->sessionData[$key] = $data;
1137
    }
1138
1139
    /**
1140
     * Sets the session data ($data) for $key and writes all session data (from ->user['ses_data']) to the database.
1141
     * The data will last only for this login session since it is stored in the session table.
1142
     *
1143
     * @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.
1144
     * @param mixed $data The data to store in index $key
1145
     */
1146
    public function setAndSaveSessionData($key, $data)
1147
    {
1148
        $this->sessionData[$key] = $data;
1149
        $this->user['ses_data'] = serialize($this->sessionData);
1150
        $this->logger->debug('setAndSaveSessionData: ses_id = ' . $this->id);
1151
        $updatedSession = $this->getSessionBackend()->update(
1152
            $this->id,
1153
            ['ses_data' => $this->user['ses_data']]
1154
        );
1155
        $this->user = array_merge($this->user ?? [], $updatedSession);
1156
    }
1157
1158
    /*************************
1159
     *
1160
     * Misc
1161
     *
1162
     *************************/
1163
    /**
1164
     * Returns an info array with Login/Logout data submitted by a form or params
1165
     *
1166
     * @return array
1167
     * @internal
1168
     */
1169
    public function getLoginFormData()
1170
    {
1171
        $loginData = [
1172
            'status' => GeneralUtility::_GP($this->formfield_status),
1173
            'uname'  => GeneralUtility::_POST($this->formfield_uname),
1174
            'uident' => GeneralUtility::_POST($this->formfield_uident)
1175
        ];
1176
        // Only process the login data if a login is requested
1177
        if ($loginData['status'] === LoginType::LOGIN) {
1178
            $loginData = $this->processLoginData($loginData);
1179
        }
1180
        return $loginData;
1181
    }
1182
1183
    /**
1184
     * Processes Login data submitted by a form or params depending on the
1185
     * passwordTransmissionStrategy
1186
     *
1187
     * @param array $loginData Login data array
1188
     * @param string $passwordTransmissionStrategy Alternative passwordTransmissionStrategy. Used when authentication services wants to override the default.
1189
     * @return array
1190
     * @internal
1191
     */
1192
    public function processLoginData($loginData, $passwordTransmissionStrategy = '')
1193
    {
1194
        $loginSecurityLevel = trim($GLOBALS['TYPO3_CONF_VARS'][$this->loginType]['loginSecurityLevel']) ?: 'normal';
1195
        $passwordTransmissionStrategy = $passwordTransmissionStrategy ?: $loginSecurityLevel;
1196
        $this->logger->debug('Login data before processing', $loginData);
1197
        $subType = 'processLoginData' . $this->loginType;
1198
        $authInfo = $this->getAuthInfoArray();
1199
        $isLoginDataProcessed = false;
1200
        $processedLoginData = $loginData;
1201
        /** @var AuthenticationService $serviceObject */
1202
        foreach ($this->getAuthServices($subType, $loginData, $authInfo) as $serviceObject) {
1203
            $serviceResult = $serviceObject->processLoginData($processedLoginData, $passwordTransmissionStrategy);
1204
            if (!empty($serviceResult)) {
1205
                $isLoginDataProcessed = true;
1206
                // If the service returns >=200 then no more processing is needed
1207
                if ((int)$serviceResult >= 200) {
1208
                    break;
1209
                }
1210
            }
1211
        }
1212
        if ($isLoginDataProcessed) {
1213
            $loginData = $processedLoginData;
1214
            $this->logger->debug('Processed login data', $processedLoginData);
1215
        }
1216
        return $loginData;
1217
    }
1218
1219
    /**
1220
     * Returns an info array which provides additional information for auth services
1221
     *
1222
     * @return array
1223
     * @internal
1224
     */
1225
    public function getAuthInfoArray()
1226
    {
1227
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->user_table);
1228
        $expressionBuilder = $queryBuilder->expr();
1229
        $authInfo = [];
1230
        $authInfo['loginType'] = $this->loginType;
1231
        $authInfo['refInfo'] = parse_url(GeneralUtility::getIndpEnv('HTTP_REFERER'));
1232
        $authInfo['HTTP_HOST'] = GeneralUtility::getIndpEnv('HTTP_HOST');
1233
        $authInfo['REMOTE_ADDR'] = GeneralUtility::getIndpEnv('REMOTE_ADDR');
1234
        $authInfo['REMOTE_HOST'] = GeneralUtility::getIndpEnv('REMOTE_HOST');
1235
        // Can be overridden in localconf by SVCONF:
1236
        $authInfo['db_user']['table'] = $this->user_table;
1237
        $authInfo['db_user']['userid_column'] = $this->userid_column;
1238
        $authInfo['db_user']['username_column'] = $this->username_column;
1239
        $authInfo['db_user']['userident_column'] = $this->userident_column;
1240
        $authInfo['db_user']['usergroup_column'] = $this->usergroup_column;
1241
        $authInfo['db_user']['enable_clause'] = $this->userConstraints()->buildExpression(
1242
            [$this->user_table => $this->user_table],
1243
            $expressionBuilder
1244
        );
1245
        if ($this->checkPid && $this->checkPid_value !== null) {
1246
            $authInfo['db_user']['checkPidList'] = $this->checkPid_value;
1247
            $authInfo['db_user']['check_pid_clause'] = $expressionBuilder->in(
1248
                'pid',
1249
                GeneralUtility::intExplode(',', (string)$this->checkPid_value)
1250
            );
1251
        } else {
1252
            $authInfo['db_user']['checkPidList'] = '';
1253
            $authInfo['db_user']['check_pid_clause'] = '';
1254
        }
1255
        $authInfo['db_groups']['table'] = $this->usergroup_table;
1256
        return $authInfo;
1257
    }
1258
1259
    /**
1260
     * Garbage collector, removing old expired sessions.
1261
     *
1262
     * @internal
1263
     */
1264
    public function gc()
1265
    {
1266
        $this->getSessionBackend()->collectGarbage($this->gc_time);
1267
    }
1268
1269
    /**
1270
     * DUMMY: Writes to log database table (in some extension classes)
1271
     *
1272
     * @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
1273
     * @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 !!)
1274
     * @param int $error flag. 0 = message, 1 = error (user problem), 2 = System Error (which should not happen), 3 = security notice (admin)
1275
     * @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
1276
     * @param string $details Default text that follows the message
1277
     * @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...
1278
     * @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.)
1279
     * @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.)
1280
     * @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.)
1281
     */
1282
    public function writelog($type, $action, $error, $details_nr, $details, $data, $tablename, $recuid, $recpid)
1283
    {
1284
    }
1285
1286
    /**
1287
     * Raw initialization of the be_user with uid=$uid
1288
     * This will circumvent all login procedures and select a be_users record from the
1289
     * database and set the content of ->user to the record selected.
1290
     * Thus the BE_USER object will appear like if a user was authenticated - however without
1291
     * a session id and the fields from the session table of course.
1292
     * Will check the users for disabled, start/endtime, etc. ($this->user_where_clause())
1293
     *
1294
     * @param int $uid The UID of the backend user to set in ->user
1295
     * @internal
1296
     */
1297
    public function setBeUserByUid($uid)
1298
    {
1299
        $this->user = $this->getRawUserByUid($uid);
1300
    }
1301
1302
    /**
1303
     * Raw initialization of the be_user with username=$name
1304
     *
1305
     * @param string $name The username to look up.
1306
     * @see \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::setBeUserByUid()
1307
     * @internal
1308
     */
1309
    public function setBeUserByName($name)
1310
    {
1311
        $this->user = $this->getRawUserByName($name);
1312
    }
1313
1314
    /**
1315
     * Fetching raw user record with uid=$uid
1316
     *
1317
     * @param int $uid The UID of the backend user to set in ->user
1318
     * @return array user record or FALSE
1319
     * @internal
1320
     */
1321
    public function getRawUserByUid($uid)
1322
    {
1323
        $query = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->user_table);
1324
        $query->setRestrictions($this->userConstraints());
1325
        $query->select('*')
1326
            ->from($this->user_table)
1327
            ->where($query->expr()->eq('uid', $query->createNamedParameter($uid, \PDO::PARAM_INT)));
1328
1329
        return $query->execute()->fetch();
1330
    }
1331
1332
    /**
1333
     * Fetching raw user record with username=$name
1334
     *
1335
     * @param string $name The username to look up.
1336
     * @return array user record or FALSE
1337
     * @see \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::getUserByUid()
1338
     * @internal
1339
     */
1340
    public function getRawUserByName($name)
1341
    {
1342
        $query = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->user_table);
1343
        $query->setRestrictions($this->userConstraints());
1344
        $query->select('*')
1345
            ->from($this->user_table)
1346
            ->where($query->expr()->eq('username', $query->createNamedParameter($name, \PDO::PARAM_STR)));
1347
1348
        return $query->execute()->fetch();
1349
    }
1350
1351
    /**
1352
     * @internal
1353
     * @return string
1354
     */
1355
    public function getSessionId(): string
1356
    {
1357
        return $this->id;
1358
    }
1359
1360
    /**
1361
     * @internal
1362
     * @return string
1363
     */
1364
    public function getLoginType(): string
1365
    {
1366
        return $this->loginType;
1367
    }
1368
1369
    /**
1370
     * Returns initialized session backend. Returns same session backend if called multiple times
1371
     *
1372
     * @return SessionBackendInterface
1373
     */
1374
    protected function getSessionBackend()
1375
    {
1376
        if (!isset($this->sessionBackend)) {
1377
            $this->sessionBackend = GeneralUtility::makeInstance(SessionManager::class)->getSessionBackend($this->loginType);
1378
        }
1379
        return $this->sessionBackend;
1380
    }
1381
}
1382