Fix a problem with devLog in user autehtication introduced with fix for #7350
[Packages/TYPO3.CMS.git] / typo3 / sysext / cms / tslib / class.tslib_feuserauth.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2008 Kasper Skaarhoj (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 * $Id$
32 * Revised for TYPO3 3.6 June/2003 by Kasper Skaarhoj
33 *
34 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
35 * @author René Fritz <r.fritz@colorcube.de>
36 */
37 /**
38 * [CLASS/FUNCTION INDEX of SCRIPT]
39 *
40 *
41 *
42 * 79: class tslib_feUserAuth extends t3lib_userAuth
43 * 143: function fetchGroupData()
44 * 233: function getUserTSconf()
45 *
46 * SECTION: Session data management functions
47 * 278: function fetchSessionData()
48 * 300: function storeSessionData()
49 * 326: function getKey($type,$key)
50 * 351: function setKey($type,$key,$data)
51 * 377: function record_registration($recs,$maxSizeOfSessionData=0)
52 *
53 * TOTAL FUNCTIONS: 7
54 * (This index is automatically created/updated by the extension "extdeveval")
55 *
56 */
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 /**
72 * Extension class for Front End User Authentication.
73 *
74 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
75 * @author René Fritz <r.fritz@colorcube.de>
76 * @package TYPO3
77 * @subpackage tslib
78 */
79 class tslib_feUserAuth extends t3lib_userAuth {
80 var $session_table = 'fe_sessions'; // Table to use for session data.
81 var $name = 'fe_typo_user'; // Session/Cookie name
82 var $get_name = 'ftu'; // Session/GET-var name
83
84 var $user_table = 'fe_users'; // Table in database with userdata
85 var $username_column = 'username'; // Column for login-name
86 var $userident_column = 'password'; // Column for password
87 var $userid_column = 'uid'; // Column for user-id
88 var $lastLogin_column = 'lastlogin';
89
90 var $enablecolumns = Array (
91 'deleted' => 'deleted',
92 'disabled' => 'disable',
93 'starttime' => 'starttime',
94 'endtime' => 'endtime'
95 );
96 var $formfield_uname = 'user'; // formfield with login-name
97 var $formfield_uident = 'pass'; // formfield with password
98 var $formfield_chalvalue = 'challenge'; // formfield with a unique value which is used to encrypt the password and username
99 var $formfield_status = 'logintype'; // formfield with status: *'login', 'logout'
100 var $formfield_permanent = 'permalogin'; // formfield with 0 or 1 // 1 = permanent login enabled // 0 = session is valid for a browser session only
101 var $security_level = ''; // sets the level of security. *'normal' = clear-text. 'challenged' = hashed password/username from form in $formfield_uident. 'superchallenged' = hashed password hashed again with username.
102
103 var $auth_include = ''; // this is the name of the include-file containing the login form. If not set, login CAN be anonymous. If set login IS needed.
104
105 var $auth_timeout_field = 6000; // Server session lifetime. If > 0: session-timeout in seconds. If false or <0: no timeout. If string: The string is a fieldname from the usertable where the timeout can be found.
106
107 var $lifetime = 0; // Client session lifetime. 0 = Session-cookies. If session-cookies, the browser will stop the session when the browser is closed. Otherwise this specifies the lifetime of a cookie that keeps the session.
108 var $sendNoCacheHeaders = 0;
109 var $getFallBack = 1; // If this is set, authentication is also accepted by the _GET. Notice that the identification is NOT 128bit MD5 hash but reduced. This is done in order to minimize the size for mobile-devices, such as WAP-phones
110 var $hash_length = 10;
111 var $getMethodEnabled = 1; // Login may be supplied by url.
112
113 var $usergroup_column = 'usergroup';
114 var $usergroup_table = 'fe_groups';
115 var $groupData = Array(
116 'title' =>Array(),
117 'uid' =>Array(),
118 'pid' =>Array()
119 );
120 var $TSdataArray=array(); // Used to accumulate the TSconfig data of the user
121 var $userTS = array();
122 var $userTSUpdated=0;
123 var $showHiddenRecords=0;
124
125 // Session and user data:
126 /*
127 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.
128 '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)
129 'Reserved' keys are:
130 - 'recs': Array: Used to 'register' records, eg in a shopping basket. Structure: [recs][tablename][record_uid]=number
131 - sys: Reserved for TypoScript standard code.
132 */
133 var $sesData = Array();
134 var $sesData_change = 0;
135 var $userData_change = 0;
136
137
138 /**
139 * Starts a user session
140 *
141 * @return void
142 * @see t3lib_userAuth::start()
143 */
144 function start() {
145 if (intval($this->auth_timeout_field)>0 && intval($this->auth_timeout_field) < $this->lifetime) {
146 // If server session timeout is non-zero but less than client session timeout: Copy this value instead.
147 $this->auth_timeout_field = $this->lifetime;
148 }
149
150 parent::start();
151 }
152
153 /**
154 * Returns a new session record for the current user for insertion into the DB.
155 *
156 * @return array user session record
157 */
158 function getNewSessionRecord($tempuser) {
159 $insertFields = parent::getNewSessionRecord($tempuser);
160 $insertFields['ses_permanent'] = $this->is_permanent;
161
162 return $insertFields;
163 }
164
165 /**
166 * Determine whether a session cookie needs to be set (lifetime=0)
167 *
168 * @return boolean
169 * @internal
170 */
171 function isSetSessionCookie() {
172 $retVal = ($this->newSessionID || $this->forceSetCookie) && ($this->lifetime==0 || !$this->user['ses_permanent']);
173 return $retVal;
174 }
175
176 /**
177 * Determine whether a non-session cookie needs to be set (lifetime>0)
178 *
179 * @return boolean
180 * @internal
181 */
182 function isRefreshTimeBasedCookie() {
183 return $this->lifetime > 0 && $this->user['ses_permanent'];
184 }
185
186 /**
187 * Returns an info array with Login/Logout data submitted by a form or params
188 *
189 * @return array
190 * @see t3lib_userAuth::getLoginFormData()
191 */
192 function getLoginFormData() {
193 $loginData = parent::getLoginFormData();
194 if($GLOBALS['TYPO3_CONF_VARS']['FE']['permalogin'] == 0 || $GLOBALS['TYPO3_CONF_VARS']['FE']['permalogin'] == 1) {
195 if ($this->getMethodEnabled) {
196 $isPermanent = t3lib_div::_GP($this->formfield_permanent);
197 } else {
198 $isPermanent = t3lib_div::_POST($this->formfield_permanent);
199 }
200 if(strlen($isPermanent) != 1) {
201 $isPermanent = $GLOBALS['TYPO3_CONF_VARS']['FE']['permalogin'];
202 } elseif(!$isPermanent) {
203 $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
204 }
205 $isPermanent = $isPermanent?1:0;
206 } elseif($GLOBALS['TYPO3_CONF_VARS']['FE']['permalogin'] == 2) {
207 $isPermanent = 1;
208 } else {
209 $isPermanent = 0;
210 }
211 $loginData['permanent'] = $isPermanent;
212 $this->is_permanent = $isPermanent;
213
214 return $loginData;
215 }
216
217 /**
218 * Will select all fe_groups records that the current fe_user is member of - and which groups are also allowed in the current domain.
219 * It also accumulates the TSconfig for the fe_user/fe_groups in ->TSdataArray
220 *
221 * @return integer Returns the number of usergroups for the frontend users (if the internal user record exists and the usergroup field contains a value)
222 */
223 function fetchGroupData() {
224 $this->TSdataArray = array();
225 $this->userTS = array();
226 $this->userTSUpdated = 0;
227 $this->groupData = Array(
228 'title' => Array(),
229 'uid' => Array(),
230 'pid' => Array()
231 );
232
233 // Setting default configuration:
234 $this->TSdataArray[]=$GLOBALS['TYPO3_CONF_VARS']['FE']['defaultUserTSconfig'];
235
236 print_r($this->user);
237 // get the info data for auth services
238 $authInfo = $this->getAuthInfoArray();
239
240 if ($this->writeDevLog) {
241 if (is_array($this->user)) {
242 t3lib_div::devLog('Get usergroups for user: '.t3lib_div::arrayToLogString($this->user, array($this->userid_column,$this->username_column)), 'tslib_feUserAuth');
243 } else {
244 t3lib_div::devLog('Get usergroups for "anonymous" user', 'tslib_feUserAuth');
245 }
246 }
247
248 $groupDataArr = array();
249
250 // use 'auth' service to find the groups for the user
251 $serviceChain='';
252 $subType = 'getGroups'.$this->loginType;
253 while (is_object($serviceObj = t3lib_div::makeInstanceService('auth', $subType, $serviceChain))) {
254 $serviceChain.=','.$serviceObj->getServiceKey();
255 $serviceObj->initAuth($subType, array(), $authInfo, $this);
256
257 $groupData = $serviceObj->getGroups($this->user, $groupDataArr);
258 if (is_array($groupData) && count($groupData)) {
259 $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.
260 }
261 unset($serviceObj);
262 }
263 if ($this->writeDevLog AND $serviceChain) t3lib_div::devLog($subType.' auth services called: '.$serviceChain, 'tslib_feUserAuth');
264 if ($this->writeDevLog AND !count($groupDataArr)) t3lib_div::devLog('No usergroups found by services', 'tslib_feUserAuth');
265 if ($this->writeDevLog AND count($groupDataArr)) t3lib_div::devLog(count($groupDataArr).' usergroup records found by services', 'tslib_feUserAuth');
266
267
268 // use 'auth' service to check the usergroups if they are really valid
269 foreach ($groupDataArr as $groupData) {
270 // by default a group is valid
271 $validGroup = TRUE;
272
273 $serviceChain='';
274 $subType = 'authGroups'.$this->loginType;
275 while (is_object($serviceObj = t3lib_div::makeInstanceService('auth', $subType, $serviceChain))) {
276 $serviceChain.=','.$serviceObj->getServiceKey();
277 $serviceObj->initAuth($subType, array(), $authInfo, $this);
278
279 if (!$serviceObj->authGroup($this->user, $groupData)) {
280 $validGroup = FALSE;
281 if ($this->writeDevLog) t3lib_div::devLog($subType.' auth service did not auth group: '.t3lib_div::arrayToLogString($groupData, 'uid,title'), 'tslib_feUserAuth', 2);
282
283 break;
284 }
285 unset($serviceObj);
286 }
287 unset($serviceObj);
288
289 if ($validGroup) {
290 $this->groupData['title'][$groupData['uid']]=$groupData['title'];
291 $this->groupData['uid'][$groupData['uid']]=$groupData['uid'];
292 $this->groupData['pid'][$groupData['uid']]=$groupData['pid'];
293 $this->groupData['TSconfig'][$groupData['uid']]=$groupData['TSconfig'];
294 }
295 }
296
297 if (count($this->groupData) && count($this->groupData['TSconfig'])) {
298 // TSconfig: collect it in the order it was collected
299 foreach($this->groupData['TSconfig'] as $TSdata) {
300 $this->TSdataArray[]=$TSdata;
301 }
302
303 $this->TSdataArray[]=$this->user['TSconfig'];
304
305 // Sort information
306 ksort($this->groupData['title']);
307 ksort($this->groupData['uid']);
308 ksort($this->groupData['pid']);
309 }
310
311 return count($this->groupData['uid']) ? count($this->groupData['uid']) : 0;
312 }
313
314 /**
315 * Returns the parsed TSconfig for the fe_user
316 * 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
317 *
318 * @return array TSconfig array for the fe_user
319 */
320 function getUserTSconf() {
321 if (!$this->userTSUpdated) {
322 // Parsing the user TS (or getting from cache)
323 $this->TSdataArray = t3lib_TSparser::checkIncludeLines_array($this->TSdataArray);
324 $userTS = implode(chr(10).'[GLOBAL]'.chr(10),$this->TSdataArray);
325 $parseObj = t3lib_div::makeInstance('t3lib_TSparser');
326 $parseObj->parse($userTS);
327 $this->userTS = $parseObj->setup;
328
329 $this->userTSUpdated=1;
330 }
331 return $this->userTS;
332 }
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350 /*****************************************
351 *
352 * Session data management functions
353 *
354 ****************************************/
355
356 /**
357 * Fetches the session data for the user (from the fe_session_data table) based on the ->id of the current user-session.
358 * The session data is restored to $this->sesData
359 * 1/100 calls will also do a garbage collection.
360 *
361 * @return void
362 * @access private
363 * @see storeSessionData()
364 */
365 function fetchSessionData() {
366 // Gets SesData if any
367 if ($this->id) {
368 $dbres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'fe_session_data', 'hash='.$GLOBALS['TYPO3_DB']->fullQuoteStr($this->id, 'fe_session_data'));
369 if ($sesDataRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($dbres)) {
370 $this->sesData = unserialize($sesDataRow['content']);
371 }
372 }
373 // delete old data:
374 if ((rand()%100) <= 1) { // a possibility of 1 % for garbage collection.
375 $GLOBALS['TYPO3_DB']->exec_DELETEquery('fe_session_data', 'tstamp < '.intval(time()-3600*24)); // all data older than 24 hours are deleted.
376 }
377 }
378
379 /**
380 * Will write UC and session data.
381 * If the flag $this->userData_change has been set, the function ->writeUC is called (which will save persistent user session data)
382 * 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)
383 *
384 * @return void
385 * @see fetchSessionData(), getKey(), setKey()
386 */
387 function storeSessionData() {
388 // Saves UC and SesData if changed.
389 if ($this->userData_change) {
390 $this->writeUC('');
391 }
392 if ($this->sesData_change) {
393 if ($this->id) {
394 $insertFields = array (
395 'hash' => $this->id,
396 'content' => serialize($this->sesData),
397 'tstamp' => time()
398 );
399 $GLOBALS['TYPO3_DB']->exec_DELETEquery('fe_session_data', 'hash='.$GLOBALS['TYPO3_DB']->fullQuoteStr($this->id, 'fe_session_data'));
400 $GLOBALS['TYPO3_DB']->exec_INSERTquery('fe_session_data', $insertFields);
401 }
402 }
403 }
404
405 /**
406 * 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)
407 *
408 * @param string Session data type; Either "user" (persistent, bound to fe_users profile) or "ses" (temporary, bound to current session cookie)
409 * @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.
410 * @return mixed Returns whatever value there was in the array for the key, $key
411 * @see setKey()
412 */
413 function getKey($type,$key) {
414 if ($key) {
415 switch($type) {
416 case 'user':
417 return $this->uc[$key];
418 break;
419 case 'ses':
420 return $this->sesData[$key];
421 break;
422 }
423 }
424 }
425
426 /**
427 * Saves session data, either persistent or bound to current session cookie. Please see getKey() for more details.
428 * 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.
429 * 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.
430 * 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.
431 *
432 * @param string Session data type; Either "user" (persistent, bound to fe_users profile) or "ses" (temporary, bound to current session cookie)
433 * @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.
434 * @param mixed The data value to store in $key
435 * @return void
436 * @see setKey(), storeSessionData(), record_registration()
437 */
438 function setKey($type,$key,$data) {
439 if ($key) {
440 switch($type) {
441 case 'user':
442 if ($this->user['uid']) {
443 $this->uc[$key]=$data;
444 $this->userData_change=1;
445 }
446 break;
447 case 'ses':
448 $this->sesData[$key]=$data;
449 $this->sesData_change=1;
450 break;
451 }
452 }
453 }
454
455 /**
456 * Registration of records/"shopping basket" in session data
457 * This will take the input array, $recs, and merge into the current "recs" array found in the session data.
458 * If a change in the recs storage happens (which it probably does) the function setKey() is called in order to store the array again.
459 *
460 * @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).
461 * @param integer The maximum size of stored session data. If zero, no limit is applied and even confirmation of cookie session is discarded.
462 * @return void
463 */
464 function record_registration($recs,$maxSizeOfSessionData=0) {
465
466 // Storing value ONLY if there is a confirmed cookie set (->cookieID), otherwise a shellscript could easily be spamming the fe_sessions table with bogus content and thus bloat the database
467 if (!$maxSizeOfSessionData || $this->cookieId===$this->id) {
468 if ($recs['clear_all']) {
469 $this->setKey('ses','recs','');
470 }
471 $change=0;
472 $recs_array=$this->getKey('ses','recs');
473 reset($recs);
474 while(list($table,$data)=each($recs)) {
475 if (is_array($data)) {
476 reset($data);
477 while(list($rec_id,$value)=each($data)) {
478 if ($value != $recs_array[$table][$rec_id]) {
479 $recs_array[$table][$rec_id] = $value;
480 $change=1;
481 }
482 }
483 }
484 }
485 if ($change && (!$maxSizeOfSessionData || strlen(serialize($recs_array))<$maxSizeOfSessionData)) {
486 $this->setKey('ses','recs',$recs_array);
487 }
488 }
489 }
490 }
491
492
493 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['tslib/class.tslib_feuserauth.php']) {
494 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['tslib/class.tslib_feuserauth.php']);
495 }
496 ?>