Passed
Push — master ( f16b47...733353 )
by
unknown
13:48
created

AbstractUserAuthentication::writelog()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Importance

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

How to fix   Many Parameters   

Many Parameters

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

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace TYPO3\CMS\Core\Authentication;
17
18
use Psr\Http\Message\ResponseInterface;
19
use Psr\Log\LoggerAwareInterface;
20
use Psr\Log\LoggerAwareTrait;
21
use Symfony\Component\HttpFoundation\Cookie;
22
use TYPO3\CMS\Core\Core\Environment;
23
use TYPO3\CMS\Core\Crypto\Random;
24
use TYPO3\CMS\Core\Database\Connection;
25
use TYPO3\CMS\Core\Database\ConnectionPool;
26
use TYPO3\CMS\Core\Database\Query\Restriction\DefaultRestrictionContainer;
27
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
28
use TYPO3\CMS\Core\Database\Query\Restriction\EndTimeRestriction;
29
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
30
use TYPO3\CMS\Core\Database\Query\Restriction\QueryRestrictionContainerInterface;
31
use TYPO3\CMS\Core\Database\Query\Restriction\RootLevelRestriction;
32
use TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction;
33
use TYPO3\CMS\Core\Exception;
34
use TYPO3\CMS\Core\Http\CookieHeaderTrait;
35
use TYPO3\CMS\Core\Session\Backend\Exception\SessionNotFoundException;
36
use TYPO3\CMS\Core\Session\UserSession;
37
use TYPO3\CMS\Core\Session\UserSessionManager;
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
     * Lifetime for the session-cookie (on the client)
138
     *
139
     * If >0: permanent cookie with given lifetime
140
     * If 0: session-cookie
141
     * Session-cookie means the browser will remove it when the browser is closed.
142
     *
143
     * @var int
144
     */
145
    protected $lifetime = 0;
146
147
    /**
148
     * Decides if the writelog() function is called at login and logout
149
     * @var bool
150
     */
151
    public $writeStdLog = false;
152
153
    /**
154
     * Log failed login attempts
155
     * @var bool
156
     */
157
    public $writeAttemptLog = false;
158
159
    /**
160
     * Send no-cache headers
161
     * @var bool
162
     */
163
    public $sendNoCacheHeaders = true;
164
165
    /**
166
     * If set, the user-record must be stored at the page defined by $checkPid_value
167
     * @var bool
168
     */
169
    public $checkPid = true;
170
171
    /**
172
     * The page id the user record must be stored at
173
     * @var int
174
     */
175
    public $checkPid_value = 0;
176
177
    /**
178
     * Will be set to TRUE if the login session is actually written during auth-check.
179
     * @var bool
180
     */
181
    public $loginSessionStarted = false;
182
183
    /**
184
     * @var array|null contains user- AND session-data from database (joined tables)
185
     * @internal
186
     */
187
    public $user;
188
189
    /**
190
     * Will force the session cookie to be set every time (lifetime must be 0)
191
     * @var bool
192
     */
193
    public $forceSetCookie = false;
194
195
    /**
196
     * Will prevent the setting of the session cookie (takes precedence over forceSetCookie)
197
     * @var bool
198
     */
199
    public $dontSetCookie = false;
200
201
    /**
202
     * Login type, used for services.
203
     * @var string
204
     */
205
    public $loginType = '';
206
207
    /**
208
     * @var array
209
     */
210
    public $uc;
211
212
    protected ?UserSession $userSession;
213
214
    protected UserSessionManager $userSessionManager;
215
216
    /**
217
     * If set, this cookie will be set to the response.
218
     *
219
     * @var Cookie|null
220
     */
221
    protected ?Cookie $setCookie;
222
223
    /**
224
     * Initialize some important variables
225
     *
226
     * @throws Exception
227
     */
228
    public function __construct()
229
    {
230
        // Backend or frontend login - used for auth services
231
        if (empty($this->loginType)) {
232
            throw new Exception('No loginType defined, must be set explicitly by subclass', 1476045345);
233
        }
234
        $this->lifetime = (int)($GLOBALS['TYPO3_CONF_VARS'][$this->loginType]['lifetime'] ?? 0);
235
    }
236
237
    /**
238
     * Currently needed for various unit tests, until start() and checkAuthentication() methods
239
     * are smaller and extracted from this class.
240
     *
241
     * @param UserSessionManager|null $userSessionManager
242
     * @internal
243
     */
244
    public function initializeUserSessionManager(?UserSessionManager $userSessionManager = null): void
245
    {
246
        $this->userSessionManager = $userSessionManager ?? UserSessionManager::create($this->loginType);
247
        $this->userSession = $this->userSessionManager->createAnonymousSession();
248
    }
249
250
    /**
251
     * Starts a user session
252
     * Typical configurations will:
253
     * a) check if session cookie was set and if not, set one,
254
     * b) check if a password/username was sent and if so, try to authenticate the user
255
     * c) Lookup a session attached to a user and check timeout etc.
256
     * d) Garbage collection, setting of no-cache headers.
257
     * If a user is authenticated the database record of the user (array) will be set in the ->user internal variable.
258
     */
259
    public function start()
260
    {
261
        $this->logger->debug('## Beginning of auth logging.');
262
        // Make certain that NO user is set initially
263
        $this->user = null;
264
265
        if (!isset($this->userSessionManager)) {
266
            $this->initializeUserSessionManager();
267
        }
268
        $this->userSession = $this->userSessionManager->createFromGlobalCookieOrAnonymous($this->name);
269
270
        // Load user session, check to see if anyone has submitted login-information and if so authenticate
271
        // the user with the session. $this->user[uid] may be used to write log...
272
        $this->checkAuthentication();
273
        // Set cookie if generally enabled or if the current session is a non-session cookie (FE permalogin)
274
        if (!$this->dontSetCookie || $this->isRefreshTimeBasedCookie()) {
275
            $this->setSessionCookie();
276
        }
277
        // Hook for alternative ways of filling the $this->user array (is used by the "timtaw" extension)
278
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['postUserLookUp'] ?? [] as $funcName) {
279
            $_params = [
280
                'pObj' => $this,
281
            ];
282
            GeneralUtility::callUserFunction($funcName, $_params, $this);
283
        }
284
    }
285
286
    /**
287
     * Used to apply a cookie to a PSR-7 Response.
288
     *
289
     * @param ResponseInterface $response
290
     * @return ResponseInterface
291
     */
292
    public function appendCookieToResponse(ResponseInterface $response): ResponseInterface
293
    {
294
        if (isset($this->setCookie)) {
295
            $response = $response->withAddedHeader('Set-Cookie', $this->setCookie->__toString());
0 ignored issues
show
Bug introduced by
The method __toString() 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

295
            $response = $response->withAddedHeader('Set-Cookie', $this->setCookie->/** @scrutinizer ignore-call */ __toString());

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...
296
        }
297
        return $response;
298
    }
299
300
    /**
301
     * Sets the session cookie for the current disposal.
302
     */
303
    protected function setSessionCookie()
304
    {
305
        $isRefreshTimeBasedCookie = $this->isRefreshTimeBasedCookie();
306
        if ($this->isSetSessionCookie() || $isRefreshTimeBasedCookie) {
307
            // Get the domain to be used for the cookie (if any):
308
            $cookieDomain = $this->getCookieDomain();
309
            // If no cookie domain is set, use the base path:
310
            $cookiePath = $cookieDomain ? '/' : GeneralUtility::getIndpEnv('TYPO3_SITE_PATH');
311
            // If the cookie lifetime is set, use it:
312
            $cookieExpire = $isRefreshTimeBasedCookie ? $GLOBALS['EXEC_TIME'] + $this->lifetime : 0;
313
            // Valid options are "strict", "lax" or "none", whereas "none" only works in HTTPS requests (default & fallback is "strict")
314
            $cookieSameSite = $this->sanitizeSameSiteCookieValue(
315
                strtolower($GLOBALS['TYPO3_CONF_VARS'][$this->loginType]['cookieSameSite'] ?? Cookie::SAMESITE_STRICT)
316
            );
317
            // Use the secure option when the current request is served by a secure connection:
318
            // SameSite "none" needs the secure option (only allowed on HTTPS)
319
            $isSecure = $cookieSameSite === Cookie::SAMESITE_NONE || GeneralUtility::getIndpEnv('TYPO3_SSL');
320
            $sessionId = $this->userSession->getIdentifier();
0 ignored issues
show
Bug introduced by
The method getIdentifier() does not exist on null. ( Ignorable by Annotation )

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

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

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

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

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

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

727
        $session = $this->userSessionManager->elevateToFixatedUserSession(/** @scrutinizer ignore-type */ $this->userSession, $tempUserId);
Loading history...
728
        // Updating lastLogin_column carrying information about last login.
729
        $this->updateLoginTimestamp($tempUserId);
730
        return $session;
731
    }
732
733
    /**
734
     * Updates the last login column in the user with the given id
735
     *
736
     * @param int $userId
737
     */
738
    protected function updateLoginTimestamp(int $userId)
739
    {
740
        if ($this->lastLogin_column) {
741
            $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->user_table);
742
            $connection->update(
743
                $this->user_table,
744
                [$this->lastLogin_column => $GLOBALS['EXEC_TIME']],
745
                [$this->userid_column => $userId]
746
            );
747
            $this->user[$this->lastLogin_column] = $GLOBALS['EXEC_TIME'];
748
        }
749
    }
750
751
    /**
752
     * Read the user session from db.
753
     *
754
     * @param bool $skipSessionUpdate
755
     * @return array|bool User session data, false if $userSession->getIdentifier() does not represent valid session
756
     * @deprecated since TYPO3 v11, will be removed in TYPO3 v12.
757
     */
758
    public function fetchUserSession($skipSessionUpdate = false)
759
    {
760
        try {
761
            $session = $this->userSessionManager->createSessionFromStorage($this->userSession->getIdentifier());
762
        } catch (SessionNotFoundException $e) {
763
            return false;
764
        }
765
        $this->userSession = $session;
766
        // Session is anonymous so no need to fetch user
767
        if ($session->isAnonymous()) {
768
            return $session->toArray();
769
        }
770
771
        // Fetch the user from the DB
772
        $userRecord = $this->fetchValidUserFromSessionOrDestroySession($skipSessionUpdate);
773
        return is_array($userRecord) ? $userRecord : false;
774
    }
775
776
    /**
777
     * If the session is bound to a user, this method fetches the user record, and returns it.
778
     * If the session has a timeout, the session date is extended if needed. Also the ìs_online
779
     * flag is updated for the user.
780
     *
781
     * However, if the session has expired the session is removed and the request is treated as an anonymous session.
782
     *
783
     * @param bool $skipSessionUpdate
784
     * @return array|null
785
     */
786
    protected function fetchValidUserFromSessionOrDestroySession(bool $skipSessionUpdate = false): ?array
787
    {
788
        if ($this->userSession->isAnonymous()) {
789
            return null;
790
        }
791
        // Fetch the user from the DB
792
        $userRecord = $this->getRawUserByUid($this->userSession->getUserId());
793
        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...
794
            // A user was found
795
            $userRecord['is_online'] = $this->userSession->getLastUpdated();
796
            if (!$this->userSessionManager->hasExpired($this->userSession)) {
0 ignored issues
show
Bug introduced by
It seems like $this->userSession can also be of type null; however, parameter $session of TYPO3\CMS\Core\Session\U...onManager::hasExpired() does only seem to accept TYPO3\CMS\Core\Session\UserSession, maybe add an additional type check? ( Ignorable by Annotation )

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

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

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

798
                    $this->userSessionManager->updateSessionTimestamp(/** @scrutinizer ignore-type */ $this->userSession);
Loading history...
799
                }
800
            } else {
801
                // Delete any user set...
802
                $this->logoff();
803
                $userRecord = false;
804
                $this->userSession = $this->userSessionManager->createAnonymousSession();
805
            }
806
        }
807
        return is_array($userRecord) ? $userRecord : null;
808
    }
809
810
    /**
811
     * Regenerates the session ID and sets the cookie again.
812
     *
813
     * @internal
814
     */
815
    public function enforceNewSessionId()
816
    {
817
        $this->regenerateSessionId();
818
        $this->setSessionCookie();
819
    }
820
821
    /**
822
     * Log out current user!
823
     * Removes the current session record, sets the internal ->user array to a blank string;
824
     * Thereby the current user (if any) is effectively logged out!
825
     */
826
    public function logoff()
827
    {
828
        $this->logger->debug('logoff: ses_id = ' . $this->userSession->getIdentifier());
829
830
        $_params = [];
831
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'] ?? [] as $_funcRef) {
832
            if ($_funcRef) {
833
                GeneralUtility::callUserFunction($_funcRef, $_params, $this);
834
            }
835
        }
836
        $this->performLogoff();
0 ignored issues
show
Bug introduced by
The method performLogoff() does not exist on null. ( Ignorable by Annotation )

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

836
        $this->/** @scrutinizer ignore-call */ 
837
               performLogoff();

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

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

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

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

1045
        $this->userSessionManager->updateSession(/** @scrutinizer ignore-type */ $this->userSession);
Loading history...
1046
    }
1047
1048
    /*************************
1049
     *
1050
     * Misc
1051
     *
1052
     *************************/
1053
    /**
1054
     * Returns an info array with Login/Logout data submitted by a form or params
1055
     *
1056
     * @return array
1057
     * @internal
1058
     */
1059
    public function getLoginFormData()
1060
    {
1061
        $loginData = [
1062
            'status' => GeneralUtility::_GP($this->formfield_status),
1063
            'uname'  => GeneralUtility::_POST($this->formfield_uname),
1064
            'uident' => GeneralUtility::_POST($this->formfield_uident)
1065
        ];
1066
        // Only process the login data if a login is requested
1067
        if ($loginData['status'] === LoginType::LOGIN) {
1068
            $loginData = $this->processLoginData($loginData);
1069
        }
1070
        return $loginData;
1071
    }
1072
1073
    /**
1074
     * Processes Login data submitted by a form or params depending on the
1075
     * passwordTransmissionStrategy
1076
     *
1077
     * @param array $loginData Login data array
1078
     * @param string $passwordTransmissionStrategy Alternative passwordTransmissionStrategy. Used when authentication services wants to override the default.
1079
     * @return array
1080
     * @internal
1081
     */
1082
    public function processLoginData($loginData, $passwordTransmissionStrategy = '')
1083
    {
1084
        $loginSecurityLevel = trim($GLOBALS['TYPO3_CONF_VARS'][$this->loginType]['loginSecurityLevel']) ?: 'normal';
1085
        $passwordTransmissionStrategy = $passwordTransmissionStrategy ?: $loginSecurityLevel;
1086
        $this->logger->debug('Login data before processing', $loginData);
1087
        $subType = 'processLoginData' . $this->loginType;
1088
        $authInfo = $this->getAuthInfoArray();
1089
        $isLoginDataProcessed = false;
1090
        $processedLoginData = $loginData;
1091
        /** @var AuthenticationService $serviceObject */
1092
        foreach ($this->getAuthServices($subType, $loginData, $authInfo) as $serviceObject) {
1093
            $serviceResult = $serviceObject->processLoginData($processedLoginData, $passwordTransmissionStrategy);
1094
            if (!empty($serviceResult)) {
1095
                $isLoginDataProcessed = true;
1096
                // If the service returns >=200 then no more processing is needed
1097
                if ((int)$serviceResult >= 200) {
1098
                    break;
1099
                }
1100
            }
1101
        }
1102
        if ($isLoginDataProcessed) {
1103
            $loginData = $processedLoginData;
1104
            $this->logger->debug('Processed login data', $processedLoginData);
1105
        }
1106
        return $loginData;
1107
    }
1108
1109
    /**
1110
     * Returns an info array which provides additional information for auth services
1111
     *
1112
     * @return array
1113
     * @internal
1114
     */
1115
    public function getAuthInfoArray()
1116
    {
1117
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->user_table);
1118
        $expressionBuilder = $queryBuilder->expr();
1119
        $authInfo = [];
1120
        $authInfo['loginType'] = $this->loginType;
1121
        $authInfo['refInfo'] = parse_url(GeneralUtility::getIndpEnv('HTTP_REFERER'));
1122
        $authInfo['HTTP_HOST'] = GeneralUtility::getIndpEnv('HTTP_HOST');
1123
        $authInfo['REMOTE_ADDR'] = GeneralUtility::getIndpEnv('REMOTE_ADDR');
1124
        $authInfo['REMOTE_HOST'] = GeneralUtility::getIndpEnv('REMOTE_HOST');
1125
        // Can be overridden in localconf by SVCONF:
1126
        $authInfo['db_user']['table'] = $this->user_table;
1127
        $authInfo['db_user']['userid_column'] = $this->userid_column;
1128
        $authInfo['db_user']['username_column'] = $this->username_column;
1129
        $authInfo['db_user']['userident_column'] = $this->userident_column;
1130
        $authInfo['db_user']['usergroup_column'] = $this->usergroup_column;
1131
        $authInfo['db_user']['enable_clause'] = $this->userConstraints()->buildExpression(
1132
            [$this->user_table => $this->user_table],
1133
            $expressionBuilder
1134
        );
1135
        if ($this->checkPid && $this->checkPid_value !== null) {
1136
            $authInfo['db_user']['checkPidList'] = $this->checkPid_value;
1137
            $authInfo['db_user']['check_pid_clause'] = $expressionBuilder->in(
1138
                'pid',
1139
                GeneralUtility::intExplode(',', (string)$this->checkPid_value)
1140
            );
1141
        } else {
1142
            $authInfo['db_user']['checkPidList'] = '';
1143
            $authInfo['db_user']['check_pid_clause'] = '';
1144
        }
1145
        $authInfo['db_groups']['table'] = $this->usergroup_table;
1146
        return $authInfo;
1147
    }
1148
1149
    /**
1150
     * DUMMY: Writes to log database table (in some extension classes)
1151
     *
1152
     * @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
1153
     * @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 !!)
1154
     * @param int $error flag. 0 = message, 1 = error (user problem), 2 = System Error (which should not happen), 3 = security notice (admin)
1155
     * @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
1156
     * @param string $details Default text that follows the message
1157
     * @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...
1158
     * @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.)
1159
     * @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.)
1160
     * @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.)
1161
     */
1162
    public function writelog($type, $action, $error, $details_nr, $details, $data, $tablename, $recuid, $recpid)
1163
    {
1164
    }
1165
1166
    /**
1167
     * Raw initialization of the be_user with uid=$uid
1168
     * This will circumvent all login procedures and select a be_users record from the
1169
     * database and set the content of ->user to the record selected.
1170
     * Thus the BE_USER object will appear like if a user was authenticated - however without
1171
     * a session id and the fields from the session table of course.
1172
     * Will check the users for disabled, start/endtime, etc. ($this->user_where_clause())
1173
     *
1174
     * @param int $uid The UID of the backend user to set in ->user
1175
     * @internal
1176
     */
1177
    public function setBeUserByUid($uid)
1178
    {
1179
        $this->user = $this->getRawUserByUid($uid);
1180
    }
1181
1182
    /**
1183
     * Raw initialization of the be_user with username=$name
1184
     *
1185
     * @param string $name The username to look up.
1186
     * @see \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::setBeUserByUid()
1187
     * @internal
1188
     */
1189
    public function setBeUserByName($name)
1190
    {
1191
        $this->user = $this->getRawUserByName($name);
1192
    }
1193
1194
    /**
1195
     * Fetching raw user record with uid=$uid
1196
     *
1197
     * @param int $uid The UID of the backend user to set in ->user
1198
     * @return array user record or FALSE
1199
     * @internal
1200
     */
1201
    public function getRawUserByUid($uid)
1202
    {
1203
        $query = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->user_table);
1204
        $query->setRestrictions($this->userConstraints());
1205
        $query->select('*')
1206
            ->from($this->user_table)
1207
            ->where($query->expr()->eq('uid', $query->createNamedParameter($uid, \PDO::PARAM_INT)));
1208
1209
        return $query->execute()->fetch();
1210
    }
1211
1212
    /**
1213
     * Fetching raw user record with username=$name
1214
     *
1215
     * @param string $name The username to look up.
1216
     * @return array user record or FALSE
1217
     * @see \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::getUserByUid()
1218
     * @internal
1219
     */
1220
    public function getRawUserByName($name)
1221
    {
1222
        $query = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->user_table);
1223
        $query->setRestrictions($this->userConstraints());
1224
        $query->select('*')
1225
            ->from($this->user_table)
1226
            ->where($query->expr()->eq('username', $query->createNamedParameter($name, \PDO::PARAM_STR)));
1227
1228
        return $query->execute()->fetch();
1229
    }
1230
1231
    /**
1232
     * @return UserSession
1233
     */
1234
    public function getSession(): UserSession
1235
    {
1236
        return $this->userSession;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->userSession could return the type null which is incompatible with the type-hinted return TYPO3\CMS\Core\Session\UserSession. Consider adding an additional type-check to rule them out.
Loading history...
1237
    }
1238
1239
    public function __isset(string $propertyName): bool
1240
    {
1241
        switch ($propertyName) {
1242
            case 'id':
1243
                trigger_error('Property id is removed in v11.', E_USER_DEPRECATED);
1244
                return isset($this->userSession);
1245
        }
1246
        return isset($this->propertyName);
0 ignored issues
show
Bug Best Practice introduced by
The property propertyName does not exist on TYPO3\CMS\Core\Authentic...tractUserAuthentication. Since you implemented __get, consider adding a @property annotation.
Loading history...
1247
    }
1248
1249
    public function __set(string $propertyName, $propertyValue)
1250
    {
1251
        switch ($propertyName) {
1252
            case 'id':
1253
                if (!isset($this->userSessionManager)) {
1254
                    $this->initializeUserSessionManager();
1255
                }
1256
                $this->userSession = UserSession::createNonFixated($propertyValue);
1257
                // No deprecation due to adaptions in testing framework to remove ->id = ...
1258
                break;
1259
        }
1260
        $this->$propertyName = $propertyValue;
1261
    }
1262
1263
    public function __get(string $propertyName)
1264
    {
1265
        switch ($propertyName) {
1266
            case 'id':
1267
                trigger_error('Property id is marked as protected now. Use ->getSession()->getIdentifier().', E_USER_DEPRECATED);
1268
                return $this->getSession()->getIdentifier();
1269
        }
1270
        return $this->$propertyName;
1271
    }
1272
1273
    public function __unset(string $propertyName): void
1274
    {
1275
        switch ($propertyName) {
1276
            case 'id':
1277
                trigger_error('Property id is marked as protected now. Use ->getSession()->getIdentifier().', E_USER_DEPRECATED);
1278
                return;
1279
        }
1280
        unset($this->$propertyName);
1281
    }
1282
}
1283