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