3 * FusionForge session management
5 * Copyright 1999-2001, VA Linux Systems, Inc.
6 * Copyright 2001-2002, 2009, Roland Mas
7 * Copyright 2004-2005, GForge, LLC
9 * This file is part of FusionForge.
11 * FusionForge is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published
13 * by the Free Software Foundation; either version 2 of the License,
14 * or (at your option) any later version.
16 * FusionForge is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with FusionForge; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 require_once $gfcommon.'include/account.php';
28 require_once $gfcommon.'include/escapingUtils.php';
31 * A User object if user is logged in
33 * @var constant $G_SESSION
40 $session_ser = getStringFromCookie('session_ser');
43 * session_build_session_cookie() - Construct session cookie for the user
45 * @param int User_id of the logged in user
46 * @return cookie value
48 function session_build_session_cookie($user_id) {
49 $session_serial = $user_id.'-*-'.time().'-*-'.getStringFromServer('REMOTE_ADDR').'-*-'.getStringFromServer('HTTP_USER_AGENT');
50 $session_serial_hash = md5($session_serial.forge_get_config('session_key'));
51 $session_serial_cookie = base64_encode($session_serial).'-*-'.$session_serial_hash;
52 return $session_serial_cookie;
56 * session_get_session_cookie_hash() - Get hash of session cookie
58 * This hash can be used as a key to identify session, e.g. in DB.
60 * @param string Value of the session cookie
63 function session_get_session_cookie_hash($session_cookie) {
64 list ($junk, $hash) = explode('-*-', $session_cookie);
69 * session_check_session_cookie() - Check that session cookie passed from user is ok
71 * @param string Value of the session cookie
72 * @return user_id if cookie is ok, false otherwise
74 function session_check_session_cookie($session_cookie) {
76 list ($session_serial, $hash) = explode('-*-', $session_cookie);
77 $session_serial = base64_decode($session_serial);
78 $new_hash = md5($session_serial.forge_get_config('session_key'));
80 if ($hash != $new_hash) {
84 list($user_id, $time, $ip, $user_agent) = explode('-*-', $session_serial, 4);
86 if (!session_check_ip($ip, getStringFromServer('REMOTE_ADDR'))) {
89 if (trim($user_agent) != getStringFromServer('HTTP_USER_AGENT')) {
92 if (($GLOBALS['sys_session_expire'] > 0) &&
93 ($time - time() >= $GLOBALS['sys_session_expire'])) {
101 * session_logout() - Log the user off the system.
103 * This function destroys object associated with the current session,
104 * making user "logged out". Deletes both user and session cookies.
109 function session_logout() {
111 // delete both session and username cookies
112 // NB: cookies must be deleted with the same scope parameters they were set with
114 session_cookie('session_ser', '');
116 RBACEngine::getInstance()->invalidateRoleCaches() ;
121 * session_login_valid() - Log the user to the system.
123 * High-level function for user login. Check credentials, and if they
124 * are valid, open new session.
126 * @param string User name
127 * @param string User password (in clear text)
128 * @param bool Allow login to non-confirmed user account (only for confirmation of the very account)
129 * @return true/false, if false reason is in global $feedback
133 function session_login_valid($loginname, $passwd, $allowpending=0) {
136 if (!$loginname || !$passwd) {
137 $feedback = _('Missing Password Or Users Name');
141 $hook_params = array () ;
142 $hook_params['loginname'] = $loginname ;
143 $hook_params['passwd'] = $passwd ;
144 plugin_hook ("session_before_login", $hook_params) ;
146 return session_login_valid_dbonly ($loginname, $passwd, $allowpending) ;
149 function session_login_valid_dbonly ($loginname, $passwd, $allowpending) {
150 global $feedback,$userstatus;
152 // Try to get the users from the database using user_id and (MD5) user_pw
153 if (forge_get_config('require_unique_email')) {
154 $res = db_query_params ('SELECT user_id,status,unix_pw FROM users WHERE (user_name=$1 OR email=$1) AND user_pw=$2',
158 $res = db_query_params ('SELECT user_id,status,unix_pw FROM users WHERE user_name=$1 AND user_pw=$2',
162 if (!$res || db_numrows($res) < 1) {
163 // No user whose MD5 passwd matches the MD5 of the provided passwd
164 // Selecting by user_name/email only
165 if (forge_get_config('require_unique_email')) {
166 $res = db_query_params ('SELECT user_id,status,unix_pw FROM users WHERE user_name=$1 OR email=$1',
167 array ($loginname)) ;
169 $res = db_query_params ('SELECT user_id,status,unix_pw FROM users WHERE user_name=$1',
170 array ($loginname)) ;
172 if (!$res || db_numrows($res) < 1) {
173 // No user by that name
174 $feedback=_('Invalid Password Or User Name');
177 // There is a user with the provided user_name/email, but the MD5 passwds do not match
178 // We'll have to try checking the (crypt) unix_pw
179 $usr = db_fetch_array($res);
180 $userstatus = $usr['status'] ;
182 if (crypt ($passwd, $usr['unix_pw']) != $usr['unix_pw']) {
183 // Even the (crypt) unix_pw does not patch
184 // This one has clearly typed a bad passwd
185 $feedback=_('Invalid Password Or User Name');
188 // User exists, (crypt) unix_pw matches
189 // Update the (MD5) user_pw and retry authentication
190 // It should work, except for status errors
191 $res = db_query_params ('UPDATE users SET user_pw=$1 WHERE user_id=$2',
194 return session_login_valid_dbonly($loginname, $passwd, $allowpending) ;
197 // If we're here, then the user has typed a password matching the (MD5) user_pw
198 // Let's check whether it also matches the (crypt) unix_pw
199 $usr = db_fetch_array($res);
201 if (crypt ($passwd, $usr['unix_pw']) != $usr['unix_pw']) {
202 // The (crypt) unix_pw does not match
203 if ($usr['unix_pw'] == '') {
204 // Empty unix_pw, we'll take the MD5 as authoritative
205 // Update the (crypt) unix_pw and retry authentication
206 // It should work, except for status errors
207 $res = db_query_params ('UPDATE users SET unix_pw=$1 WHERE user_id=$2',
208 array (account_genunixpw($passwd),
210 return session_login_valid_dbonly($loginname, $passwd, $allowpending) ;
212 // Invalidate (MD5) user_pw, refuse authentication
213 $res = db_query_params ('UPDATE users SET user_pw=$1 WHERE user_id=$2',
214 array ('OUT OF DATE',
216 $feedback=_('Invalid Password Or User Name');
221 // Yay. The provided password matches both fields in the database.
222 // Let's check the status of this user
224 // if allowpending (for verify.php) then allow
225 $userstatus=$usr['status'];
226 if ($allowpending && ($usr['status'] == 'P')) {
229 if ($usr['status'] == 'S') {
231 $feedback = _('Account Suspended');
234 if ($usr['status'] == 'P') {
236 $feedback = _('Account Pending');
239 if ($usr['status'] == 'D') {
241 $feedback = _('Account Deleted');
244 if ($usr['status'] != 'A') {
245 //unacceptable account flag
246 $feedback = _('Account Not Active');
250 //create a new session
251 session_set_new(db_result($res,0,'user_id'));
258 * session_check_ip() - Check 2 IP addresses for match
260 * This function checks that IP addresses match
262 * IPv4 addresses are allowed to match with some
263 * fuzz factor (within 255.255.0.0 subnet).
265 * For IPv6 addresses, no fuzz is needed since there's
266 * usually no NAT in IPv6.
268 * @param string The old IP address
269 * @param string The new IP address
273 function session_check_ip($oldip,$newip) {
274 if (strstr ($oldip, ':')) {
276 if (strstr ($newip, ':')) {
277 // New IP is IPv6 too
278 return ($oldip == $newip) ;
284 if (strstr ($newip, ':')) {
288 $eoldip = explode(".",$oldip);
289 $enewip = explode(".",$newip);
291 // require same class b subnet
292 return ( ($eoldip[0] == $enewip[0])
293 && ($eoldip[1] == $enewip[1]) ) ;
299 * session_issecure() - Check if current session is secure
304 function session_issecure() {
305 return (strtoupper(getStringFromServer('HTTPS')) == "ON");
309 * session_cookie() - Set a session cookie
311 * Set a cookie with default temporal scope of the current browser session
312 * and URL space of the current webserver
314 * @param string Name of cookie
315 * @param string Value of cookie
316 * @param string Domain scope (default '')
317 * @param string Expiration time in UNIX seconds (default 0)
320 function session_cookie($name ,$value, $domain = '', $expiration = 0) {
321 if (php_sapi_name() != 'cli') {
322 if ( $expiration != 0){
323 setcookie($name, $value, time() + $expiration, '/', $domain, 0);
325 setcookie($name, $value, $expiration, '/', $domain, 0);
331 * session_redirect() - Redirect browser within the site
333 * @param string Absolute path within the site
334 * @return never returns
336 function session_redirect($loc) {
337 header('Location: '.util_make_url ($loc));
343 * session_require() - DEPRECATED Convenience function to easily enforce permissions
345 * Calling page will terminate with error message if current user
348 * @param array Associative array specifying criteria
349 * @return does not return if check is failed
352 function session_require($req, $reason='') {
353 if (!session_loggedin()) {
354 exit_not_logged_in();
357 $user =& user_get_object(user_getid());
358 if (! $user->isActive()) {
360 exit_error('Error','Your account is no longer active ; you have been disconnected');
363 if (array_key_exists('group', $req)) {
364 $group =& group_get_object($req['group']);
365 if (!$group || !is_object($group)) {
366 exit_error('Error',$reason == '' ? _('Could Not Get Group') : $reason);
367 } elseif ($group->isError()) {
368 exit_error('Error',$reason == '' ? $group->getErrorMessage() : $reason);
371 $perm =& $group->getPermission ();
372 if (!$perm || !is_object($perm) || $perm->isError()) {
373 exit_permission_denied($reason);
376 if (isset($req['admin_flags']) && $req['admin_flags']) {
377 if (!$perm->isAdmin()) {
378 exit_permission_denied($reason);
381 if (!$perm->isMember()) {
382 exit_permission_denied($reason);
385 } else if ($req['isloggedin']) {
386 //no need to check as long as the check is present at top of function
388 exit_permission_denied($reason);
393 * session_require_perm() - Convenience function to easily enforce permissions
395 * Calling page will terminate with error message if current user
399 function session_require_perm ($section, $reference, $action = NULL, $reason='') {
400 if (!forge_check_perm ($section, $reference, $action)) {
401 exit_permission_denied ($reason);
406 * session_require_global_perm() - Convenience function to easily enforce permissions
408 * Calling page will terminate with error message if current user
412 function session_require_global_perm ($section, $action = NULL, $reason='') {
413 if (!forge_check_global_perm ($section, $action)) {
414 exit_permission_denied ($reason);
419 * session_require_login() - Convenience function to easily enforce permissions
421 * Calling page will terminate with error message if current user
425 function session_require_login () {
426 if (!session_loggedin()) {
427 exit_not_logged_in () ;
432 * session_set_new() - Setup session for the given user
434 * This function sets up SourceForge session for the given user,
435 * making one be "logged in".
437 * @param int The user ID
440 function session_set_new($user_id) {
441 global $G_SESSION,$session_ser;
443 // set session cookie
445 $cookie = session_build_session_cookie($user_id);
446 session_cookie("session_ser", $cookie, "", $GLOBALS['sys_session_expire']);
447 $session_ser=$cookie;
449 $res = db_query_params ('SELECT count(*) as c FROM user_session WHERE session_hash =$1',
450 array (session_get_session_cookie_hash($cookie))) ;
451 if (!$res || db_result($res,0,'c') < 1) {
452 db_query_params ('INSERT INTO user_session (session_hash,ip_addr,time,user_id) VALUES ($1,$2,$3,$4)',
453 array (session_get_session_cookie_hash($cookie),
454 getStringFromServer('REMOTE_ADDR'),
459 // check uniqueness of the session_hash in the database
461 $res = session_getdata($user_id);
464 exit_error(_('ERROR'),_('ERROR').": ".db_error());
466 else if (db_numrows($res) < 1) {
467 exit_error(_('ERROR'),_('ERROR').": could not fetch user session data");
470 //set up the new user object
472 $G_SESSION = user_get_object($user_id,$res);
474 $G_SESSION->setLoggedIn(true);
478 RBACEngine::getInstance()->invalidateRoleCaches() ;
483 * session_set_admin() - Setup session for the admin user
485 * This function sets up a session for the administrator
489 function session_set_admin() {
490 $res = db_query_params ('SELECT user_id FROM user_group WHERE admin_flags=$1 AND group_id=1',
496 if (db_numrows($res) == 0) {
497 echo "No admin users?\n" ;
500 $id = db_result ($res, 0, 0);
501 session_set_new ($id);
505 * Private optimization function for logins - fetches user data, language, and session
508 * @param int The user ID
511 function session_getdata($user_id) {
512 return db_query_params ('SELECT u.*,sl.language_id, sl.name, sl.filename, sl.classname, sl.language_code, t.dirname, t.fullname
513 FROM users u, supported_languages sl, themes t
514 WHERE u.language=sl.language_id
515 AND u.theme_id=t.theme_id
521 * session_set() - Re-initialize session for the logged in user
523 * This function checks that the user is logged in and if so, initialize
524 * internal session environment.
528 function session_set() {
529 plugin_hook('session_set_entry');
531 global $session_ser, $session_key;
533 // assume bad session_hash and session. If all checks work, then allow
534 // otherwise make new session
537 // If user says he's logged in (by presenting cookie), check that
540 $user_id = session_check_session_cookie($session_ser);
544 $result = session_getdata($user_id);
546 if (db_numrows($result) > 0) {
550 } // else (hash does not exist) or (session hash is bad)
553 $G_SESSION = user_get_object($user_id, $result);
555 $G_SESSION->setLoggedIn(true);
560 // if there was bad session cookie, kill it and the user cookie
566 plugin_hook('session_set_return');
568 RBACEngine::getInstance()->invalidateRoleCaches() ;
571 //TODO - this should be generalized and used for pre.php,
572 //SOAP, forum_gateway.php, tracker_gateway.php, etc to
574 function session_continue($sessionKey) {
576 $session_ser = $sessionKey;
578 setup_gettext_from_context();
579 $LUSER =& session_get_user();
580 if (!is_object($LUSER) || $LUSER->isError()) {
583 putenv('TZ='. $LUSER->getTimeZone());
589 * session_get_user() - Wrapper function to return the User object for the logged in user.
594 function &session_get_user() {
601 * Get user_id of logged in user
604 function user_getid() {
607 return $G_SESSION->getID();
615 * See if user is logged in
617 function session_loggedin() {
621 return $G_SESSION->isLoggedIn();
629 // c-file-style: "bsd"