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

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