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. FusionForge is free software;
10 * you can redistribute it and/or modify it under the terms of the
11 * GNU General Public License as published by the Free Software
12 * Foundation; either version 2 of the Licence, or (at your option)
15 * FusionForge is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License along
21 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 require_once $gfcommon.'include/account.php';
26 require_once $gfcommon.'include/utils.php';
27 require_once $gfcommon.'include/escapingUtils.php';
30 * A User object if user is logged in
32 * @var constant $G_SESSION
39 $session_ser = getStringFromCookie('session_ser');
42 * session_build_session_cookie() - Construct session cookie for the user
44 * @param int User_id of the logged in user
45 * @return cookie value
47 function session_build_session_cookie($user_id) {
48 $session_serial = $user_id.'-*-'.time().'-*-'.getStringFromServer('REMOTE_ADDR').'-*-'.getStringFromServer('HTTP_USER_AGENT');
49 $session_serial_hash = md5($session_serial.forge_get_config('session_key'));
50 $session_serial_cookie = base64_encode($session_serial).'-*-'.$session_serial_hash;
51 return $session_serial_cookie;
55 * session_get_session_cookie_hash() - Get hash of session cookie
57 * This hash can be used as a key to identify session, e.g. in DB.
59 * @param string Value of the session cookie
62 function session_get_session_cookie_hash($session_cookie) {
63 list ($junk, $hash) = explode('-*-', $session_cookie);
68 * session_check_session_cookie() - Check that session cookie passed from user is ok
70 * @param string Value of the session cookie
71 * @return user_id if cookie is ok, false otherwise
73 function session_check_session_cookie($session_cookie) {
75 list ($session_serial, $hash) = explode('-*-', $session_cookie);
76 $session_serial = base64_decode($session_serial);
77 $new_hash = md5($session_serial.forge_get_config('session_key'));
79 if ($hash != $new_hash) {
83 list($user_id, $time, $ip, $user_agent) = explode('-*-', $session_serial, 4);
85 if (!session_check_ip($ip, getStringFromServer('REMOTE_ADDR'))) {
88 if (trim($user_agent) != getStringFromServer('HTTP_USER_AGENT')) {
91 if ((forge_get_config('session_expire') > 0) &&
92 ($time - time() >= forge_get_config('session_expire'))) {
100 * session_logout() - Log the user off the system.
102 * This function destroys object associated with the current session,
103 * making user "logged out". Deletes both user and session cookies.
108 function session_logout() {
110 // delete both session and username cookies
111 // NB: cookies must be deleted with the same scope parameters they were set with
113 session_cookie('session_ser', '');
115 RBACEngine::getInstance()->invalidateRoleCaches() ;
120 * session_login_valid() - Log the user to the system.
122 * High-level function for user login. Check credentials, and if they
123 * are valid, open new session.
125 * @param string User name
126 * @param string User password (in clear text)
127 * @param bool Allow login to non-confirmed user account (only for confirmation of the very account)
128 * @return true/false, if false reason is in global $feedback
132 function session_login_valid($loginname, $passwd, $allowpending=0) {
133 global $feedback,$error_msg,$warning_msg;
135 if (!$loginname || !$passwd) {
136 $warning_msg = _('Missing Password Or Users Name');
140 $hook_params = array () ;
141 $hook_params['loginname'] = $loginname ;
142 $hook_params['passwd'] = $passwd ;
143 $result = plugin_hook ("session_before_login", $hook_params) ;
145 // Refuse login if not all the plugins are ok.
148 $warning_msg = _('Invalid Password Or User Name');
153 return session_login_valid_dbonly ($loginname, $passwd, $allowpending) ;
156 function session_login_valid_dbonly ($loginname, $passwd, $allowpending) {
157 global $feedback,$userstatus;
159 // Try to get the users from the database using user_id and (MD5) user_pw
160 if (forge_get_config('require_unique_email')) {
161 $res = db_query_params ('SELECT user_id,status,unix_pw FROM users WHERE (user_name=$1 OR email=$1) AND user_pw=$2',
165 $res = db_query_params ('SELECT user_id,status,unix_pw FROM users WHERE user_name=$1 AND user_pw=$2',
169 if (!$res || db_numrows($res) < 1) {
170 // No user whose MD5 passwd matches the MD5 of the provided passwd
171 // Selecting by user_name/email only
172 if (forge_get_config('require_unique_email')) {
173 $res = db_query_params ('SELECT user_id,status,unix_pw FROM users WHERE user_name=$1 OR email=$1',
174 array ($loginname)) ;
176 $res = db_query_params ('SELECT user_id,status,unix_pw FROM users WHERE user_name=$1',
177 array ($loginname)) ;
179 if (!$res || db_numrows($res) < 1) {
180 // No user by that name
181 $feedback=_('Invalid Password Or User Name');
184 // There is a user with the provided user_name/email, but the MD5 passwds do not match
185 // We'll have to try checking the (crypt) unix_pw
186 $usr = db_fetch_array($res);
187 $userstatus = $usr['status'] ;
189 if (crypt ($passwd, $usr['unix_pw']) != $usr['unix_pw']) {
190 // Even the (crypt) unix_pw does not patch
191 // This one has clearly typed a bad passwd
192 $feedback=_('Invalid Password Or User Name');
195 // User exists, (crypt) unix_pw matches
196 // Update the (MD5) user_pw and retry authentication
197 // It should work, except for status errors
198 $res = db_query_params ('UPDATE users SET user_pw=$1 WHERE user_id=$2',
201 return session_login_valid_dbonly($loginname, $passwd, $allowpending) ;
204 // If we're here, then the user has typed a password matching the (MD5) user_pw
205 // Let's check whether it also matches the (crypt) unix_pw
206 $usr = db_fetch_array($res);
208 if (crypt ($passwd, $usr['unix_pw']) != $usr['unix_pw']) {
209 // The (crypt) unix_pw does not match
210 if ($usr['unix_pw'] == '') {
211 // Empty unix_pw, we'll take the MD5 as authoritative
212 // Update the (crypt) unix_pw and retry authentication
213 // It should work, except for status errors
214 $res = db_query_params ('UPDATE users SET unix_pw=$1 WHERE user_id=$2',
215 array (account_genunixpw($passwd),
217 return session_login_valid_dbonly($loginname, $passwd, $allowpending) ;
219 // Invalidate (MD5) user_pw, refuse authentication
220 $res = db_query_params ('UPDATE users SET user_pw=$1 WHERE user_id=$2',
221 array ('OUT OF DATE',
223 $feedback=_('Invalid Password Or User Name');
228 // Yay. The provided password matches both fields in the database.
229 // Let's check the status of this user
231 // if allowpending (for verify.php) then allow
232 $userstatus=$usr['status'];
233 if ($allowpending && ($usr['status'] == 'P')) {
236 if ($usr['status'] == 'S') {
238 $feedback = _('Account Suspended');
241 if ($usr['status'] == 'P') {
243 $feedback = _('Account Pending');
246 if ($usr['status'] == 'D') {
248 $feedback = _('Account Deleted');
251 if ($usr['status'] != 'A') {
252 //unacceptable account flag
253 $feedback = _('Account Not Active');
257 //create a new session
258 session_set_new(db_result($res,0,'user_id'));
265 * session_check_ip() - Check 2 IP addresses for match
267 * This function checks that IP addresses match
269 * IPv4 addresses are allowed to match with some
270 * fuzz factor (within 255.255.0.0 subnet).
272 * For IPv6 addresses, no fuzz is needed since there's
273 * usually no NAT in IPv6.
275 * @param string The old IP address
276 * @param string The new IP address
280 function session_check_ip($oldip,$newip) {
281 if (strstr ($oldip, ':')) {
283 if (strstr ($newip, ':')) {
284 // New IP is IPv6 too
285 return ($oldip == $newip) ;
291 if (strstr ($newip, ':')) {
295 $eoldip = explode(".",$oldip);
296 $enewip = explode(".",$newip);
298 // require same class b subnet
299 return ( ($eoldip[0] == $enewip[0])
300 && ($eoldip[1] == $enewip[1]) ) ;
306 * session_issecure() - Check if current session is secure
311 function session_issecure() {
312 return (strtoupper(getStringFromServer('HTTPS')) == "ON");
316 * session_cookie() - Set a session cookie
318 * Set a cookie with default temporal scope of the current browser session
319 * and URL space of the current webserver
321 * @param string Name of cookie
322 * @param string Value of cookie
323 * @param string Domain scope (default '')
324 * @param string Expiration time in UNIX seconds (default 0)
327 function session_cookie($name ,$value, $domain = '', $expiration = 0) {
328 if (php_sapi_name() != 'cli') {
329 if ( $expiration != 0){
330 setcookie($name, $value, time() + $expiration, '/', $domain, 0);
332 setcookie($name, $value, $expiration, '/', $domain, 0);
338 * session_redirect_uri() - Redirect browser
340 * @param string Absolute URI
341 * @return never returns
343 function session_redirect_uri($loc) {
344 sysdebug_off("Status: 301 Moved Permanently", true, 301);
345 header("Location: ${loc}", true);
346 echo "\nPlease go to ${loc} instead!\n";
351 * session_redirect() - Redirect browser within the site
353 * @param string Absolute path within the site
354 * @return never returns
356 function session_redirect($loc) {
357 session_redirect_uri(util_make_url($loc));
361 * session_require() - DEPRECATED Convenience function to easily enforce permissions
363 * Calling page will terminate with error message if current user
366 * @param array Associative array specifying criteria
367 * @return does not return if check is failed
370 function session_require($req, $reason='') {
371 if (!session_loggedin()) {
372 exit_not_logged_in();
375 $user =& user_get_object(user_getid());
376 if (! $user->isActive()) {
378 exit_error(_('Your account is no longer active ; you have been disconnected'),'');
381 if (array_key_exists('group', $req)) {
382 $group = group_get_object($req['group']);
383 if (!$group || !is_object($group)) {
385 } elseif ($group->isError()) {
386 exit_error($reason == '' ? $group->getErrorMessage() : $reason, '');
389 $perm =& $group->getPermission ();
390 if (!$perm || !is_object($perm) || $perm->isError()) {
391 exit_permission_denied($reason,'');
394 if (isset($req['admin_flags']) && $req['admin_flags']) {
395 if (!$perm->isAdmin()) {
396 exit_permission_denied($reason,'');
399 if (!$perm->isMember()) {
400 exit_permission_denied($reason,'');
404 exit_permission_denied($reason,'');
409 * session_require_perm() - Convenience function to easily enforce permissions
411 * Calling page will terminate with error message if current user
415 function session_require_perm ($section, $reference, $action = NULL, $reason='') {
416 if (!forge_check_perm ($section, $reference, $action)) {
417 exit_permission_denied ($reason,'');
422 * session_require_global_perm() - Convenience function to easily enforce permissions
424 * Calling page will terminate with error message if current user
428 function session_require_global_perm ($section, $action = NULL, $reason='') {
429 if (!forge_check_global_perm ($section, $action)) {
431 $reason = sprintf (_('Permission denied. The %s administrators will have to grant you permission to view this page.'),
432 forge_get_config ('forge_name')) ;
434 exit_permission_denied ($reason,'');
439 * session_require_login() - Convenience function to easily enforce permissions
441 * Calling page will terminate with error message if current user
445 function session_require_login () {
446 if (!session_loggedin()) {
447 exit_not_logged_in () ;
452 * session_set_new() - Setup session for the given user
454 * This function sets up SourceForge session for the given user,
455 * making one be "logged in".
457 * @param int The user ID
460 function session_set_new($user_id) {
463 // set session cookie
465 $cookie = session_build_session_cookie($user_id);
466 session_cookie("session_ser", $cookie, "", forge_get_config('session_expire'));
467 $session_ser=$cookie;
469 $res = db_query_params ('SELECT count(*) as c FROM user_session WHERE session_hash =$1',
470 array (session_get_session_cookie_hash($cookie))) ;
471 if (!$res || db_result($res,0,'c') < 1) {
472 db_query_params ('INSERT INTO user_session (session_hash,ip_addr,time,user_id) VALUES ($1,$2,$3,$4)',
473 array (session_get_session_cookie_hash($cookie),
474 getStringFromServer('REMOTE_ADDR'),
479 // check uniqueness of the session_hash in the database
481 $res = session_getdata($user_id);
484 exit_error(db_error(),'');
486 else if (db_numrows($res) < 1) {
487 exit_error(_('Could not fetch user session data'),'');
489 session_set_internal ($user_id, $res) ;
493 function session_set_internal ($user_id, $res=false) {
496 $G_SESSION = user_get_object($user_id,$res);
498 $G_SESSION->setLoggedIn(true);
501 RBACEngine::getInstance()->invalidateRoleCaches() ;
506 * session_set_admin() - Setup session for the admin user
508 * This function sets up a session for the administrator
512 function session_set_admin() {
513 $admins = RBACEngine::getInstance()->getUsersByAllowedAction ('forge_admin', -1) ;
514 if (count ($admins) == 0) {
515 exit_error(_('No admin users ?'),'');
517 session_set_new ($admins[0]->getID());
521 * Private optimization function for logins - fetches user data, language, and session
524 * @param int The user ID
527 function session_getdata($user_id) {
528 return db_query_params ('SELECT u.*,sl.language_id, sl.name, sl.filename, sl.classname, sl.language_code, t.dirname, t.fullname
529 FROM users u, supported_languages sl, themes t
530 WHERE u.language=sl.language_id
531 AND u.theme_id=t.theme_id
537 * session_set() - Re-initialize session for the logged in user
539 * This function checks that the user is logged in and if so, initialize
540 * internal session environment.
544 function session_set() {
545 plugin_hook('session_set_entry');
549 // assume bad session_hash and session. If all checks work, then allow
550 // otherwise make new session
553 // If user says he's logged in (by presenting cookie), check that
556 $user_id = session_check_session_cookie($session_ser);
560 $result = session_getdata($user_id);
562 if (db_numrows($result) > 0) {
566 } // else (hash does not exist) or (session hash is bad)
569 $G_SESSION = user_get_object($user_id, $result);
571 $G_SESSION->setLoggedIn(true);
576 // if there was bad session cookie, kill it and the user cookie
582 plugin_hook('session_set_return');
584 RBACEngine::getInstance()->invalidateRoleCaches() ;
587 //TODO - this should be generalized and used for pre.php,
588 //SOAP, forum_gateway.php, tracker_gateway.php, etc to
590 function session_continue($sessionKey) {
592 $session_ser = $sessionKey;
594 setup_gettext_from_context();
595 setup_tz_from_context();
596 $LUSER =& session_get_user();
597 if (!is_object($LUSER) || $LUSER->isError()) {
604 function setup_tz_from_context() {
605 $LUSER =& session_get_user();
606 if (!is_object($LUSER) || $LUSER->isError()) {
607 $tz = forge_get_config('default_timezone');
609 $tz = $LUSER->getTimeZone();
612 date_default_timezone_set($tz);
616 * session_get_user() - Wrapper function to return the User object for the logged in user.
621 function &session_get_user() {
628 * Get user_id of logged in user
631 function user_getid() {
634 return $G_SESSION->getID();
642 * See if user is logged in
644 function session_loggedin() {
648 return $G_SESSION->isLoggedIn();
656 // c-file-style: "bsd"