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