[BUGFIX] Do not remove FE session cookie if fe_user is logged in
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / Authentication / FrontendUserAuthentication.php
1 <?php
2 namespace TYPO3\CMS\Frontend\Authentication;
3
4 /**
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16 use TYPO3\CMS\Core\Utility\GeneralUtility;
17
18 /**
19 * Extension class for Front End User Authentication.
20 *
21 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
22 * @author René Fritz <r.fritz@colorcube.de>
23 */
24 class FrontendUserAuthentication extends \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication {
25
26 /**
27 * form field with 0 or 1
28 * 1 = permanent login enabled
29 * 0 = session is valid for a browser session only
30 * @var string
31 * @todo Define visibility
32 */
33 public $formfield_permanent = 'permalogin';
34
35 /**
36 * Lifetime of session data in seconds.
37 * @var int
38 */
39 protected $sessionDataLifetime = 86400;
40
41 /**
42 * @var string
43 * @todo Define visibility
44 */
45 public $usergroup_column = 'usergroup';
46
47 /**
48 * @var string
49 * @todo Define visibility
50 */
51 public $usergroup_table = 'fe_groups';
52
53 /**
54 * @var array
55 * @todo Define visibility
56 */
57 public $groupData = array(
58 'title' => array(),
59 'uid' => array(),
60 'pid' => array()
61 );
62
63 /**
64 * Used to accumulate the TSconfig data of the user
65 * @var array
66 * @todo Define visibility
67 */
68 public $TSdataArray = array();
69
70 /**
71 * @var array
72 * @todo Define visibility
73 */
74 public $userTS = array();
75
76 /**
77 * @var bool
78 * @todo Define visibility
79 */
80 public $userTSUpdated = FALSE;
81
82 /**
83 * Session and user data:
84 * There are two types of data that can be stored: UserData and Session-Data.
85 * Userdata is for the login-user, and session-data for anyone viewing the pages.
86 * 'Keys' are keys in the internal data array of the data.
87 * When you get or set a key in one of the data-spaces (user or session) you decide the type of the variable (not object though)
88 * 'Reserved' keys are:
89 * - 'recs': Array: Used to 'register' records, eg in a shopping basket. Structure: [recs][tablename][record_uid]=number
90 * - sys: Reserved for TypoScript standard code.
91 * @todo Define visibility
92 */
93 public $sesData = array();
94
95 /**
96 * @var bool
97 * @todo Define visibility
98 */
99 public $sesData_change = FALSE;
100
101 /**
102 * @var bool
103 * @todo Define visibility
104 */
105 public $userData_change = FALSE;
106
107 /**
108 * @var bool
109 */
110 public $is_permanent;
111
112 /**
113 * @var int|NULL
114 */
115 protected $sessionDataTimestamp = NULL;
116
117 /**
118 * @var bool
119 */
120 protected $loginHidden = FALSE;
121
122 /**
123 * Default constructor.
124 */
125 public function __construct() {
126 parent::__construct();
127
128 // Disable cookie by default, will be activated if saveSessionData() is called,
129 // a user is logging-in or an existing session is found
130 $this->dontSetCookie = TRUE;
131
132 $this->session_table = 'fe_sessions';
133 $this->name = self::getCookieName();
134 $this->get_name = 'ftu';
135 $this->loginType = 'FE';
136 $this->user_table = 'fe_users';
137 $this->username_column = 'username';
138 $this->userident_column = 'password';
139 $this->userid_column = 'uid';
140 $this->lastLogin_column = 'lastlogin';
141 $this->enablecolumns = array(
142 'deleted' => 'deleted',
143 'disabled' => 'disable',
144 'starttime' => 'starttime',
145 'endtime' => 'endtime'
146 );
147 $this->formfield_uname = 'user';
148 $this->formfield_uident = 'pass';
149 $this->formfield_chalvalue = 'challenge';
150 $this->formfield_status = 'logintype';
151 $this->auth_timeout_field = 6000;
152 $this->sendNoCacheHeaders = FALSE;
153 $this->getFallBack = TRUE;
154 $this->getMethodEnabled = TRUE;
155 }
156
157 /**
158 * Returns the configured cookie name
159 *
160 * @return string
161 */
162 static public function getCookieName() {
163 $configuredCookieName = trim($GLOBALS['TYPO3_CONF_VARS']['FE']['cookieName']);
164 if (empty($configuredCookieName)) {
165 $configuredCookieName = 'fe_typo_user';
166 }
167 return $configuredCookieName;
168 }
169
170 /**
171 * Starts a user session
172 *
173 * @return void
174 * @see \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::start()
175 * @todo Define visibility
176 */
177 public function start() {
178 if ((int)$this->auth_timeout_field > 0 && (int)$this->auth_timeout_field < $this->lifetime) {
179 // If server session timeout is non-zero but less than client session timeout: Copy this value instead.
180 $this->auth_timeout_field = $this->lifetime;
181 }
182 $this->sessionDataLifetime = (int)$GLOBALS['TYPO3_CONF_VARS']['FE']['sessionDataLifetime'];
183 if ($this->sessionDataLifetime <= 0) {
184 $this->sessionDataLifetime = 86400;
185 }
186 parent::start();
187 }
188
189 /**
190 * Returns a new session record for the current user for insertion into the DB.
191 *
192 * @param array $tempuser
193 * @return array User session record
194 * @todo Define visibility
195 */
196 public function getNewSessionRecord($tempuser) {
197 $insertFields = parent::getNewSessionRecord($tempuser);
198 $insertFields['ses_permanent'] = $this->is_permanent;
199 return $insertFields;
200 }
201
202 /**
203 * Determine whether a session cookie needs to be set (lifetime=0)
204 *
205 * @return boolean
206 * @internal
207 * @todo Define visibility
208 */
209 public function isSetSessionCookie() {
210 return ($this->newSessionID || $this->forceSetCookie)
211 && ($this->lifetime == 0 || !isset($this->user['ses_permanent']) || !$this->user['ses_permanent']);
212 }
213
214 /**
215 * Determine whether a non-session cookie needs to be set (lifetime>0)
216 *
217 * @return boolean
218 * @internal
219 * @todo Define visibility
220 */
221 public function isRefreshTimeBasedCookie() {
222 return $this->lifetime > 0 && isset($this->user['ses_permanent']) && $this->user['ses_permanent'];
223 }
224
225 /**
226 * Returns an info array with Login/Logout data submitted by a form or params
227 *
228 * @return array
229 * @see \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication::getLoginFormData()
230 * @todo Define visibility
231 */
232 public function getLoginFormData() {
233 $loginData = parent::getLoginFormData();
234 if ($GLOBALS['TYPO3_CONF_VARS']['FE']['permalogin'] == 0 || $GLOBALS['TYPO3_CONF_VARS']['FE']['permalogin'] == 1) {
235 if ($this->getMethodEnabled) {
236 $isPermanent = GeneralUtility::_GP($this->formfield_permanent);
237 } else {
238 $isPermanent = GeneralUtility::_POST($this->formfield_permanent);
239 }
240 if (strlen($isPermanent) != 1) {
241 $isPermanent = $GLOBALS['TYPO3_CONF_VARS']['FE']['permalogin'];
242 } elseif (!$isPermanent) {
243 // To make sure the user gets a session cookie and doesn't keep a possibly existing time based cookie,
244 // we need to force seeting the session cookie here
245 $this->forceSetCookie = TRUE;
246 }
247 $isPermanent = $isPermanent ? 1 : 0;
248 } elseif ($GLOBALS['TYPO3_CONF_VARS']['FE']['permalogin'] == 2) {
249 $isPermanent = 1;
250 } else {
251 $isPermanent = 0;
252 }
253 $loginData['permanent'] = $isPermanent;
254 $this->is_permanent = $isPermanent;
255 return $loginData;
256 }
257
258 /**
259 * Creates a user session record and returns its values.
260 * However, as the FE user cookie is normally not set, this has to be done
261 * before the parent class is doing the rest.
262 *
263 * @param array $tempuser User data array
264 * @return array The session data for the newly created session.
265 */
266 public function createUserSession($tempuser) {
267 // At this point we do not know if we need to set a session or a "permanant" cookie
268 // So we force the cookie to be set after authentication took place, which will
269 // then call setSessionCookie(), which will set a cookie with correct settings.
270 $this->dontSetCookie = FALSE;
271 return parent::createUserSession($tempuser);
272 }
273
274 /**
275 * Will select all fe_groups records that the current fe_user is member of
276 * and which groups are also allowed in the current domain.
277 * It also accumulates the TSconfig for the fe_user/fe_groups in ->TSdataArray
278 *
279 * @return integer Returns the number of usergroups for the frontend users (if the internal user record exists and the usergroup field contains a value)
280 * @todo Define visibility
281 */
282 public function fetchGroupData() {
283 $this->TSdataArray = array();
284 $this->userTS = array();
285 $this->userTSUpdated = FALSE;
286 $this->groupData = array(
287 'title' => array(),
288 'uid' => array(),
289 'pid' => array()
290 );
291 // Setting default configuration:
292 $this->TSdataArray[] = $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultUserTSconfig'];
293 // Get the info data for auth services
294 $authInfo = $this->getAuthInfoArray();
295 if ($this->writeDevLog) {
296 if (is_array($this->user)) {
297 GeneralUtility::devLog('Get usergroups for user: ' . GeneralUtility::arrayToLogString($this->user, array($this->userid_column, $this->username_column)), 'TYPO3\\CMS\\Frontend\\Authentication\\FrontendUserAuthentication');
298 } else {
299 GeneralUtility::devLog('Get usergroups for "anonymous" user', 'TYPO3\\CMS\\Frontend\\Authentication\\FrontendUserAuthentication');
300 }
301 }
302 $groupDataArr = array();
303 // Use 'auth' service to find the groups for the user
304 $serviceChain = '';
305 $subType = 'getGroups' . $this->loginType;
306 while (is_object($serviceObj = GeneralUtility::makeInstanceService('auth', $subType, $serviceChain))) {
307 $serviceChain .= ',' . $serviceObj->getServiceKey();
308 $serviceObj->initAuth($subType, array(), $authInfo, $this);
309 $groupData = $serviceObj->getGroups($this->user, $groupDataArr);
310 if (is_array($groupData) && count($groupData)) {
311 // Keys in $groupData should be unique ids of the groups (like "uid") so this function will override groups.
312 $groupDataArr = GeneralUtility::array_merge($groupDataArr, $groupData);
313 }
314 unset($serviceObj);
315 }
316 if ($this->writeDevLog && $serviceChain) {
317 GeneralUtility::devLog($subType . ' auth services called: ' . $serviceChain, 'TYPO3\\CMS\\Frontend\\Authentication\\FrontendUserAuthentication');
318 }
319 if ($this->writeDevLog && !count($groupDataArr)) {
320 GeneralUtility::devLog('No usergroups found by services', 'TYPO3\\CMS\\Frontend\\Authentication\\FrontendUserAuthentication');
321 }
322 if ($this->writeDevLog && count($groupDataArr)) {
323 GeneralUtility::devLog(count($groupDataArr) . ' usergroup records found by services', 'TYPO3\\CMS\\Frontend\\Authentication\\FrontendUserAuthentication');
324 }
325 // Use 'auth' service to check the usergroups if they are really valid
326 foreach ($groupDataArr as $groupData) {
327 // By default a group is valid
328 $validGroup = TRUE;
329 $serviceChain = '';
330 $subType = 'authGroups' . $this->loginType;
331 while (is_object($serviceObj = GeneralUtility::makeInstanceService('auth', $subType, $serviceChain))) {
332 $serviceChain .= ',' . $serviceObj->getServiceKey();
333 $serviceObj->initAuth($subType, array(), $authInfo, $this);
334 if (!$serviceObj->authGroup($this->user, $groupData)) {
335 $validGroup = FALSE;
336 if ($this->writeDevLog) {
337 GeneralUtility::devLog($subType . ' auth service did not auth group: ' . GeneralUtility::arrayToLogString($groupData, 'uid,title'), 'TYPO3\\CMS\\Frontend\\Authentication\\FrontendUserAuthentication', 2);
338 }
339 break;
340 }
341 unset($serviceObj);
342 }
343 unset($serviceObj);
344 if ($validGroup) {
345 $this->groupData['title'][$groupData['uid']] = $groupData['title'];
346 $this->groupData['uid'][$groupData['uid']] = $groupData['uid'];
347 $this->groupData['pid'][$groupData['uid']] = $groupData['pid'];
348 $this->groupData['TSconfig'][$groupData['uid']] = $groupData['TSconfig'];
349 }
350 }
351 if (count($this->groupData) && count($this->groupData['TSconfig'])) {
352 // TSconfig: collect it in the order it was collected
353 foreach ($this->groupData['TSconfig'] as $TSdata) {
354 $this->TSdataArray[] = $TSdata;
355 }
356 $this->TSdataArray[] = $this->user['TSconfig'];
357 // Sort information
358 ksort($this->groupData['title']);
359 ksort($this->groupData['uid']);
360 ksort($this->groupData['pid']);
361 }
362 return count($this->groupData['uid']) ?: 0;
363 }
364
365 /**
366 * Returns the parsed TSconfig for the fe_user
367 * The TSconfig will be cached in $this->userTS.
368 *
369 * @return array TSconfig array for the fe_user
370 * @todo Define visibility
371 */
372 public function getUserTSconf() {
373 if (!$this->userTSUpdated) {
374 // Parsing the user TS (or getting from cache)
375 $this->TSdataArray = \TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser::checkIncludeLines_array($this->TSdataArray);
376 $userTS = implode(LF . '[GLOBAL]' . LF, $this->TSdataArray);
377 $parseObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\TypoScript\\Parser\\TypoScriptParser');
378 $parseObj->parse($userTS);
379 $this->userTS = $parseObj->setup;
380 $this->userTSUpdated = TRUE;
381 }
382 return $this->userTS;
383 }
384
385 /*****************************************
386 *
387 * Session data management functions
388 *
389 ****************************************/
390 /**
391 * Fetches the session data for the user (from the fe_session_data table) based on the ->id of the current user-session.
392 * The session data is restored to $this->sesData
393 * 1/100 calls will also do a garbage collection.
394 *
395 * @return void
396 * @access private
397 * @see storeSessionData()
398 * @todo Define visibility
399 */
400 public function fetchSessionData() {
401 // Gets SesData if any AND if not already selected by session fixation check in ->isExistingSessionRecord()
402 if ($this->id && !count($this->sesData)) {
403 $statement = $this->db->prepare_SELECTquery('*', 'fe_session_data', 'hash = :hash');
404 $statement->execute(array(':hash' => $this->id));
405 if (($sesDataRow = $statement->fetch()) !== FALSE) {
406 $this->sesData = unserialize($sesDataRow['content']);
407 $this->sessionDataTimestamp = $sesDataRow['tstamp'];
408 }
409 $statement->free();
410 }
411 }
412
413 /**
414 * Will write UC and session data.
415 * If the flag $this->userData_change has been set, the function ->writeUC is called (which will save persistent user session data)
416 * If the flag $this->sesData_change has been set, the fe_session_data table is updated with the content of $this->sesData
417 * If the $this->sessionDataTimestamp is NULL there was no session record yet, so we need to insert it into the database
418 *
419 * @return void
420 * @see fetchSessionData(), getKey(), setKey()
421 * @todo Define visibility
422 */
423 public function storeSessionData() {
424 // Saves UC and SesData if changed.
425 if ($this->userData_change) {
426 $this->writeUC('');
427 }
428 if ($this->sesData_change && $this->id) {
429 if (empty($this->sesData)) {
430 // Remove session-data
431 $this->removeSessionData();
432 // Remove cookie if not logged in as the session data is removed as well
433 if (empty($this->user['uid']) && !$this->loginHidden && $this->isCookieSet()) {
434 $this->removeCookie($this->name);
435 }
436 } elseif ($this->sessionDataTimestamp === NULL) {
437 // Write new session-data
438 $insertFields = array(
439 'hash' => $this->id,
440 'content' => serialize($this->sesData),
441 'tstamp' => $GLOBALS['EXEC_TIME']
442 );
443 $this->sessionDataTimestamp = $GLOBALS['EXEC_TIME'];
444 $this->db->exec_INSERTquery('fe_session_data', $insertFields);
445 // Now set the cookie (= fix the session)
446 $this->setSessionCookie();
447 } else {
448 // Update session data
449 $updateFields = array(
450 'content' => serialize($this->sesData),
451 'tstamp' => $GLOBALS['EXEC_TIME']
452 );
453 $this->sessionDataTimestamp = $GLOBALS['EXEC_TIME'];
454 $this->db->exec_UPDATEquery('fe_session_data', 'hash=' . $this->db->fullQuoteStr($this->id, 'fe_session_data'), $updateFields);
455 }
456 }
457 }
458
459 /**
460 * Removes data of the current session.
461 *
462 * @return void
463 */
464 public function removeSessionData() {
465 $this->db->exec_DELETEquery('fe_session_data', 'hash=' . $this->db->fullQuoteStr($this->id, 'fe_session_data'));
466 }
467
468 /**
469 * Log out current user!
470 * Removes the current session record, sets the internal ->user array to a blank string
471 * Thereby the current user (if any) is effectively logged out!
472 * Additionally the cookie is removed
473 *
474 * @return void
475 */
476 public function logoff() {
477 parent::logoff();
478 // Remove the cookie on log-off, but only if we do not have an anonymous session
479 if (!$this->isExistingSessionRecord($this->id) && $this->isCookieSet()) {
480 $this->removeCookie($this->name);
481 }
482 }
483
484 /**
485 * Executes the garbage collection of session data and session.
486 * The lifetime of session data is defined by $TYPO3_CONF_VARS['FE']['sessionDataLifetime'].
487 *
488 * @return void
489 */
490 public function gc() {
491 $timeoutTimeStamp = (int)($GLOBALS['EXEC_TIME'] - $this->sessionDataLifetime);
492 $this->db->exec_DELETEquery('fe_session_data', 'tstamp < ' . $timeoutTimeStamp);
493 parent::gc();
494 }
495
496 /**
497 * Returns session data for the fe_user; Either persistent data following the fe_users uid/profile (requires login)
498 * or current-session based (not available when browse is closed, but does not require login)
499 *
500 * @param string $type Session data type; Either "user" (persistent, bound to fe_users profile) or "ses" (temporary, bound to current session cookie)
501 * @param string $key Key from the data array to return; The session data (in either case) is an array ($this->uc / $this->sesData) and this value determines which key to return the value for.
502 * @return mixed Returns whatever value there was in the array for the key, $key
503 * @see setKey()
504 * @todo Define visibility
505 */
506 public function getKey($type, $key) {
507 if (!$key) {
508 return NULL;
509 }
510 $value = NULL;
511 switch ($type) {
512 case 'user':
513 $value = $this->uc[$key];
514 break;
515 case 'ses':
516 $value = $this->sesData[$key];
517 break;
518 }
519 return $value;
520 }
521
522 /**
523 * Saves session data, either persistent or bound to current session cookie. Please see getKey() for more details.
524 * When a value is set the flags $this->userData_change or $this->sesData_change will be set so that the final call to ->storeSessionData() will know if a change has occurred and needs to be saved to the database.
525 * Notice: The key "recs" is already used by the function record_registration() which stores table/uid=value pairs in that key. This is used for the shopping basket among other things.
526 * Notice: Simply calling this function will not save the data to the database! The actual saving is done in storeSessionData() which is called as some of the last things in index_ts.php. So if you exit before this point, nothing gets saved of course! And the solution is to call $GLOBALS['TSFE']->storeSessionData(); before you exit.
527 *
528 * @param string $type Session data type; Either "user" (persistent, bound to fe_users profile) or "ses" (temporary, bound to current session cookie)
529 * @param string $key Key from the data array to store incoming data in; The session data (in either case) is an array ($this->uc / $this->sesData) and this value determines in which key the $data value will be stored.
530 * @param mixed $data The data value to store in $key
531 * @return void
532 * @see setKey(), storeSessionData(), record_registration()
533 * @todo Define visibility
534 */
535 public function setKey($type, $key, $data) {
536 if (!$key) {
537 return;
538 }
539 switch ($type) {
540 case 'user':
541 if ($this->user['uid']) {
542 if ($data === NULL) {
543 unset($this->uc[$key]);
544 } else {
545 $this->uc[$key] = $data;
546 }
547 $this->userData_change = TRUE;
548 }
549 break;
550 case 'ses':
551 if ($data === NULL) {
552 unset($this->sesData[$key]);
553 } else {
554 $this->sesData[$key] = $data;
555 }
556 $this->sesData_change = TRUE;
557 break;
558 }
559 }
560
561 /**
562 * Returns the session data stored for $key.
563 * The data will last only for this login session since it is stored in the session table.
564 *
565 * @param string $key
566 * @return mixed
567 */
568 public function getSessionData($key) {
569 return $this->getKey('ses', $key);
570 }
571
572 /**
573 * Saves the tokens so that they can be used by a later incarnation of this class.
574 *
575 * @param string $key
576 * @param mixed $data
577 * @return void
578 */
579 public function setAndSaveSessionData($key, $data) {
580 $this->setKey('ses', $key, $data);
581 $this->storeSessionData();
582 }
583
584 /**
585 * Registration of records/"shopping basket" in session data
586 * This will take the input array, $recs, and merge into the current "recs" array found in the session data.
587 * If a change in the recs storage happens (which it probably does) the function setKey() is called in order to store the array again.
588 *
589 * @param array $recs The data array to merge into/override the current recs values. The $recs array is constructed as [table]][uid] = scalar-value (eg. string/integer).
590 * @param integer $maxSizeOfSessionData The maximum size of stored session data. If zero, no limit is applied and even confirmation of cookie session is discarded.
591 * @return void
592 * @todo Define visibility
593 */
594 public function record_registration($recs, $maxSizeOfSessionData = 0) {
595 // Storing value ONLY if there is a confirmed cookie set,
596 // otherwise a shellscript could easily be spamming the fe_sessions table
597 // with bogus content and thus bloat the database
598 if (!$maxSizeOfSessionData || $this->isCookieSet()) {
599 if ($recs['clear_all']) {
600 $this->setKey('ses', 'recs', array());
601 }
602 $change = 0;
603 $recs_array = $this->getKey('ses', 'recs');
604 foreach ($recs as $table => $data) {
605 if (is_array($data)) {
606 foreach ($data as $rec_id => $value) {
607 if ($value != $recs_array[$table][$rec_id]) {
608 $recs_array[$table][$rec_id] = $value;
609 $change = 1;
610 }
611 }
612 }
613 }
614 if ($change && (!$maxSizeOfSessionData || strlen(serialize($recs_array)) < $maxSizeOfSessionData)) {
615 $this->setKey('ses', 'recs', $recs_array);
616 }
617 }
618 }
619
620 /**
621 * Determine whether there's an according session record to a given session_id
622 * in the database. Don't care if session record is still valid or not.
623 *
624 * This calls the parent function but additionally tries to look up the session ID in the "fe_session_data" table.
625 *
626 * @param integer $id Claimed Session ID
627 * @return boolean Returns TRUE if a corresponding session was found in the database
628 * @todo Define visibility
629 */
630 public function isExistingSessionRecord($id) {
631 // Perform check in parent function
632 $count = parent::isExistingSessionRecord($id);
633 // Check if there are any fe_session_data records for the session ID the client claims to have
634 if ($count == FALSE) {
635 $statement = $this->db->prepare_SELECTquery('content,tstamp', 'fe_session_data', 'hash = :hash');
636 $res = $statement->execute(array(':hash' => $id));
637 if ($res !== FALSE) {
638 if ($sesDataRow = $statement->fetch()) {
639 $count = TRUE;
640 $this->sesData = unserialize($sesDataRow['content']);
641 $this->sessionDataTimestamp = $sesDataRow['tstamp'];
642 }
643 $statement->free();
644 }
645 }
646 return $count;
647 }
648
649 /**
650 * Hide the current login
651 *
652 * This is used by the fe_login_mode feature for pages.
653 * A current login is unset, but we remember that there has been one.
654 *
655 * @return void
656 */
657 public function hideActiveLogin() {
658 $this->user = NULL;
659 $this->loginHidden = TRUE;
660 }
661
662 }