Location: PHPKode > projects > OpenNitro > trunk/Nitro/Session.inc.php
<?php
//
// +---------------------------------------------------------------------------+
// | Nitro :: Session                                                          |
// +---------------------------------------------------------------------------+
// | Copyright (c) 2003 June Systems BV                                        |
// +---------------------------------------------------------------------------+
// | This library is free software; you can redistribute it and/or modify it   |
// | under the terms of the GNU Lesser General Public License as published by  |
// | the Free Software Foundation; either version 2.1 of the License, or (at   |
// | your option) any later version.                                           |
// |                                                                           |
// | This library is distributed in the hope that it will be useful, but       |
// | WITHOUT ANY WARRANTY; without even the implied warranty of                |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser   |
// | General Public License for more details.                                  |
// |                                                                           |
// | You should have received a copy of the GNU Lesser General Public License  |
// | along with this library; if not, write to the Free Software Foundation,   |
// | Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA            |
// +---------------------------------------------------------------------------+
// | Authors: Siggi Oskarsson <hide@address.com>                          |
// +---------------------------------------------------------------------------+
//
// $Id: Session.inc.php 229 2008-04-17 09:20:31Z oli $
//
// This file contains the session handlers for Nitro
//

/**
 * This file contains the Session class of Nitro
 *
 * @package	Nitro
 * @subpackage	Base
 * @author 		Siggi Oskarsson
 * @version 	$Revision: 1.31 $
 * @copyright	2004 June Systems BV
 */

define ('_DEF_SESSIONTIMEOUT', 20); // minutes
define ('_DEF_LOGINTEMPLATE', 'Defaults/Templates/Login.tpl');

/**
 * NitroSession class
 *
 * @package	Nitro
 * @subpackage	Base
 */
class NitroSession {
	var $DB;
	var $Conf;
	var $UserLoginModule;

	var $UserID;
	var $User = Array();
	var $LoggedOn = FALSE;
	var $LoginSalt;
	var $SecurityGroups;
	var $SecurityGroupsLogout;
	var $SessionUserAgent;
	var $SessionStart;
	var $SessionLastView;

	var $LoginFailed;
	var $SessionExpired;

	var $Language;

	var $CacheConfig;
	var $PageStack = Array();

	function NitroSession() {
		global $DB, $__NitroConf;
		
		// If SessionExpired is posted than session jues expired (needed for wrong passwd re-attempt)
		if ($_POST['SessionExpired']) $this->SessionExpired = 1;
		$this->DB = &$DB;
		$this->Conf = &$__NitroConf;
	}
	
	function Login($username, $password, $salt = FALSE) {
		// Check whether we need to restore GET and POST variables when session expired
		if ($_POST["SessionGET"]) {
			$GETVars = unserialize(base64_decode($_POST["SessionGET"]));
			if (is_array($GETVars)) {
				global $_GET;
				foreach($GETVars AS $Key => $Values) {
					$_GET[$Key] = $Values;
				}
			}
		}
		if ($_POST["SessionPOST"]) {
			$POSTVars = unserialize(base64_decode($_POST["SessionPOST"]));
			if (is_array($POSTVars)) {
				global $_POST;
				foreach($POSTVars AS $Key => $Values) {
					if ($Key != "_Nitro_Login_username" && $Key != "_Nitro_Login_password")
						$_POST[$Key] = $Values;
				}
			}
		}

		if ($username && $password) {
			DebugGroup("Session", "Login", "Login attempted", __FILE__, __LINE__, DEBUG_SESS_OK);
			if ($this->Conf->CONF["Settings"]["UserLoginModule"]) {
				#######################################################################
				# User login module used
				#
				# This uses an external module in the Nitro/Session/ Directory to
				# handle the login and checksession functions.
				#######################################################################
				Debug("Session", "Login", "Logging in with custom user module:".$this->Conf->CONF["Settings"]["UserLoginModule"], __FILE__, __LINE__, DEBUG_SESS_OK);
				if (is_object($this->UserLoginModule)) {
					$User = $this->UserLoginModule->Login($username, $password);
				} else {
					$this->Error = "Login class (NitroSession_".$this->Conf->CONF["Settings"]["UserLoginModule"].") defined in settings is not initalized!";
					$this->RaiseFatalError();
				}
			} else {
				if ($this->Conf->CONF["Settings"]["UserTable"]) {
					#######################################################################
					# Custom user table used
					#
					# This uses a custom database table, but the same principle login
					# method as the default Nitro login.
					#######################################################################
					Debug("Session", "Login", "Using custom user table:".$this->Conf->CONF["Settings"]["UserTable"], __FILE__, __LINE__, DEBUG_SESS_OK);
					if ($ParsedUserTable = $this->ParseUserTableString($this->Conf->CONF["Settings"]["UserTable"])) {
						$DBAlias = $ParsedUserTable["DBAlias"];
						$Query = "SELECT ".($ParsedUserTable["Columns"] ? implode(",", $ParsedUserTable["Columns"]) : $ParsedUserTable["Username"]).",
												".$ParsedUserTable["Password"]." AS Password
											FROM ".$ParsedUserTable["Table"]."
											WHERE ".$ParsedUserTable["Username"]." = ".NitroPrepareDB($username)."
												AND ".$ParsedUserTable["Password"]." = ".NitroPrepareDB(sha1($password))."
											".($ParsedUserTable["WHERE"] ? " AND ".implode(" AND ", $ParsedUserTable["WHERE"]) : "")."
											";
						//echo $Query;print_r($ParsedUserTable["WHERE"]);
					} else {
						Debug("Session", "Login", "User table definition is incorrect", __FILE__, __LINE__, DEBUG_SESS_ERR);
						$this->Error = "User table definition is incorrect";
						return FALSE;
					}
				} else {
					#######################################################################
					# Default Nitro login code
					#######################################################################
					Debug("Session", "Login", "Using default user table", __FILE__, __LINE__, DEBUG_SESS_OK);
					$DBAlias = "Nitro";
					// password = md5(real_password + salt)
					// $this->LoginSalt
					$Query = "SELECT UserID, Name, Password
										FROM User
										WHERE Username = ".NitroPrepareDB($username)."
											AND Password = ".NitroPrepareDB(sha1($password))."
										";
				}
				//echo $this->DB[$DBAlias]->PrepareQuery($Query);exit;
				$Result = $this->DB[$DBAlias]->query($Query);
				if ($Result->numRows()) {
					Debug("Session", "Login", "Login successful, setting session vars", __FILE__, __LINE__, DEBUG_SESS_OK);
					$User = $Result->fetchArray();
				} else {
					Debug("Session", "Login", "Login failed", __FILE__, __LINE__, DEBUG_SESS_ERR);
					$this->LoginFailed = TRUE;
				}
				$Result->free();
			}

			#######################################################################
			# Complete login if successfull
			#######################################################################
			if (is_array($User) && $User['UserID']) {
				// Log on successful, call startSession
				$this->startSession($User);
			} else {
				// login attempted, but failed, sleep 2 seconds to slow down hackers
				sleep(2);
			}

			DebugCloseGroup(DEBUG_SESS_OK);
		} else {
			Debug("Session", "Login", "Login attempted with empty username or password", __FILE__, __LINE__, DEBUG_SESS_ERR);
			$this->LoginFailed = TRUE;
		}

		$this->LoginSalt = FALSE;

		return ((is_array($User) && $User['UserID']) ? TRUE : FALSE);
	}

	/**
	 * Start the Nitro session after successfull logon
	 *
	 * This function sets the necessary session variables,
	 * regenerates the session id, loads the user info, retreives
	 * the user security groups and fills the login stack if exists.
	 *
	 * @param	array	$User	Array of user variables ($User['UserID'] mandatory!)
	 */
	function startSession($User)
	{
		DebugGroup(__CLASS__, __FUNCTION__, "Starting session", __FILE__, __LINE__, DEBUG_SESS_OK);
		$this->LoggedOn = TRUE;
		$this->LoginFailed = FALSE;
		$this->SessionStart = time();
		$this->SessionUserAgent = md5($_SERVER["HTTP_USER_AGENT"]);

		$this->UserID = $User['UserID'];
		$this->User = Array();
		$this->LoadUserSettings();
		foreach($User AS $ID => $Value) {
			$this->User[$ID] = $Value;
		}
		Debug(__CLASS__, __FUNCTION__, "Setting security groups", __FILE__, __LINE__, DEBUG_SESS_OK);
		$this->SecurityGroups = NitroGetSecurityGroups($this->UserID);

		if (count($this->SecurityGroups)) {
			// check whether session expired
			if ($_POST['SessionExpired'] OR $this->SessionExpired) {
				// continue session & no login stack
				Debug(__CLASS__, __FUNCTION__, "Session expired, skipping login stack", __FILE__, __LINE__, DEBUG_SESS_OK);
				$this->SessionExpired = 0;
			} else {
				Debug(__CLASS__, __FUNCTION__, "Setting login page stack for user to walk through", __FILE__, __LINE__, DEBUG_SESS_OK);
				$Query = "SELECT
										P.PageID, P.IDString, LSP.Condition, LS.ForceLastStack, LS.LoginStackID
									FROM SecurityGroup AS SG
									INNER JOIN LoginStack AS LS
										 ON LS.LoginStackID = SG.LoginStackID
									INNER JOIN LoginStackPage AS LSP
										 ON LSP.LoginStackID = LS.LoginStackID
									INNER JOIN Page_SecurityGroup AS PSG
										 ON PSG.SecurityGroupID = SG.SecurityGroupID
										AND PSG.PageID = LSP.PageID
									INNER JOIN Page AS P
										 ON P.PageID = LSP.PageID
									WHERE SG.SecurityGroupID IN (".implode(",", $this->SecurityGroups).")
									GROUP BY P.PageID, P.IDString, LS.LoginStackID, LSP.Condition, LS.ForceLastStack, LS.Sortorder, LSP.Sortorder
									ORDER BY LS.Sortorder, LSP.Sortorder DESC
									";
									//echo $Query;exit;
				$Result = $this->DB["Nitro"]->query($Query);
				$n = 0;
				$LoginStackID = FALSE;
				while($Row = $Result->fetchArray()) {
					if (!$LoginStackID OR $LoginStackID == $Row["LoginStackID"]) {
						// Check whether ForceLastStack is true and then add the current
						// Page to the stack as the exit point
						if (!$n && !$Row["ForceLastStack"]) {
							array_push($this->PageStack, $_GET["P"]);
						}
						// Check if condition is used and if it is true
						if($Row["Condition"]) {
							include_once "Nitro/Condition.inc.php";
							$Condition = CheckCondition($Row["Condition"]);
						} else {
							$Condition = TRUE;
						}
						// Add Page to the stack if condition is true
						if ($Condition) {
							array_push($this->PageStack, $Row["IDString"]);
						}
						$n++;
					} else {
						// Next login stack, break loop and continue with script
						break;
					}
					$LoginStackID = $Row["LoginStackID"];
				}
				$Result->free();
			}
		}

		// Regenerate session id and delete old session data
		Debug(__CLASS__, __FUNCTION__, "Regenerating session id", __FILE__, __LINE__, DEBUG_SESS_OK);
		NitroRegenerateSessionID();
		Debug(__CLASS__, __FUNCTION__, "New session id: ".session_id(), __FILE__, __LINE__, DEBUG_SESS_OK);

		DebugCloseGroup(DEBUG_SESS_OK);
	}

	/**
	 * Parse user table string
	 *
	 * This function parses a user table string and returns it in an
	 * array the login function recongnizes. Format:
	 *
	 * [DBAlias]:[Table]:[Username Column,Password Column]:[WHERE statement]:[Other columns to get]
	 *
	 * @param	$UserTable	User table string definition
	 */
	function ParseUserTableString($UserTable){
		$UserTable = explode(":", $UserTable);
		if (count($UserTable) == 5) {
			$RV = Array();
			$RV["DBAlias"] = $UserTable[0];
			$RV["Table"] = $UserTable[1];
			$RV["Username"] = current(explode(",", $UserTable[2]));
			$RV["Password"] = end(explode(",", $UserTable[2]));
			$RV["WHERE"] = ($UserTable[3] ? explode(",", $UserTable[3]) : FALSE);
			if (is_array($RV["WHERE"])) {
				foreach($RV["WHERE"] AS $key => $where) {
					// fix for ini failure with = signs, must use something else like -->
					$clause = explode('-->', $where);
					if (!ereg('[<>=]', $clause[1]))	$RV["WHERE"][$key] = $clause[0].' = '.$clause[1];
					else 														$RV["WHERE"][$key] = $clause[0].' '.$clause[1];
				}
			}
			$RV["Columns"] = explode(",", $UserTable[4]);
		} else {
			$RV = FALSE;
		}
		return $RV;
	}

	function CheckSession(){
		Debug("Session", "CheckSession", "Checking session validity: ".session_id(), __FILE__, __LINE__, DEBUG_SESS_OK);
		// If a custom session module is used, include it and set in the class
		if ($this->Conf->CONF["Settings"]["UserLoginModule"]) {
			Debug("Session", "NitroSession", "Using custom user module:".$this->Conf->CONF["Settings"]["UserLoginModule"], __FILE__, __LINE__, DEBUG_SESS_OK);
			$includeFile = 'Nitro/Session/'.$this->Conf->CONF["Settings"]["UserLoginModule"].'.inc.php';
			// Let PHP find the include file for us
			if (@include_once($includeFile)) {
				if (class_exists('NitroSession_'.$this->Conf->CONF["Settings"]["UserLoginModule"])) {
					eval('$this->UserLoginModule = new NitroSession_'.$this->Conf->CONF["Settings"]["UserLoginModule"].'($this);');
					if (!is_object($this->UserLoginModule)) {
						$this->Error = "Login class (NitroSession_".$this->Conf->CONF["Settings"]["UserLoginModule"].") defined in settings does not return a valid object!";
						$this->RaiseFatalError();
					}
				} else {
					$this->Error = "Login class (NitroSession_".$this->Conf->CONF["Settings"]["UserLoginModule"].") defined in settings does not exist!";
					$this->RaiseFatalError();
				}
			} else {
				$this->Error = "Login module (".$this->Conf->CONF["Settings"]["UserLoginModule"].") defined in settings does not exist!";
				$this->RaiseFatalError();
			}
			$this->UserLoginModule->CheckSession();
		} else {
			Debug("Session", "NitroSession", "Using default Nitro user module", __FILE__, __LINE__, DEBUG_SESS_OK);
			// Max session duration in seconds, conf is in minutes, default = 20
			$MaxSession = 60*($this->Conf->CONF["Settings"]["SessionTimeout"] ? $this->Conf->CONF["Settings"]["SessionTimeout"] : _DEF_SESSIONTIMEOUT);
			if ($this->UserID && ((time() - $this->SessionLastView) > $MaxSession)) {
				Debug("Session", "CheckSession", "Session expired", __FILE__, __LINE__, DEBUG_SESS_ERR);
				$this->SessionExpired = TRUE;
				$this->Logout();
			}
			//TODO: - Add token to cookie => $token = md5(uniqid(rand(), true));
			if ($this->UserID && $this->SessionUserAgent != md5($_SERVER["HTTP_USER_AGENT"])) {
				Debug("Session", "CheckSession", "User agent has changed, spoofing???", __FILE__, __LINE__, DEBUG_SESS_ERR);
				$this->Logout();
			}
			$this->SessionLastView = time();
		}
	}
	
	function Logout() {
		Debug("Session", "Logout", "Logout called, killing session", __FILE__, __LINE__, DEBUG_SESS_OK);

		$this->SaveUserSettings();
		$this->LoggedOn = FALSE;
		$this->UserID = FALSE;
		$this->User = Array();
		$this->SecurityGroups = $this->SecurityGroupsLogout;
		$this->Name = FALSE;

		// Regenerate session id and delete old session data
		NitroRegenerateSessionID();

		return TRUE;
	}

	function LoadUserSettings() {
		global $__NSess;

		DebugGroup("Session", "LoadUserSettings", "Loading User Settings", __FILE__, __LINE__, DEBUG_SESS_OK);
		if ($this->UserID && $this->Conf->CONF["Settings"]["UserSaveSettings"]) {
			$UserSaveSettings = explode(":", $this->Conf->CONF["Settings"]["UserSaveSettings"]);
			if (count($UserSaveSettings) == 4) {
				Debug("Session", "LoadUserSettings", "Settings column defined (correctly) in config file", __FILE__, __LINE__, DEBUG_SESS_OK);
				$SettingsDBAlias = $UserSaveSettings[0];
				$SettingsTable = $UserSaveSettings[1];
				$SettingsUserID = $UserSaveSettings[2];
				$SettingsColumn = $UserSaveSettings[3];
				$Query = "SELECT $SettingsColumn FROM $SettingsTable
									WHERE $SettingsUserID = $this->UserID
									";
									//echo $Query;
				$Settings = unserialize($this->DB[$SettingsDBAlias]->getOne($Query));
				if (is_array($Settings)) {
					Debug("Session", "LoadUserSettings", "Saved settings loaded", __FILE__, __LINE__, DEBUG_SESS_OK);
					$this->User = $Settings;
					$RV = TRUE;
				} else {
					Debug("Session", "LoadUserSettings", "No previous saved settings found", __FILE__, __LINE__, DEBUG_SESS_OK);
					$RV = FALSE;
				}
			} else {
				Debug("Session", "LoadUserSettings", "Settings column not defined (correctly) in config file", __FILE__, __LINE__, DEBUG_SESS_ERR);
				$RV = FALSE;
			}
		}
		DebugCloseGroup(DEBUG_SESS_OK);
		return $RV;
	}

	function SaveUserSettings() {
		DebugGroup("Session", "SaveUserSettings", "Saveing User Settings", __FILE__, __LINE__, DEBUG_SESS_OK);

		$RV = FALSE;

		if ($this->UserID && array_key_exists('UserSaveSettings', $this->Conf->CONF["Settings"]) && $this->Conf->CONF["Settings"]["UserSaveSettings"]) {
			$UserSaveSettings = explode(":", $this->Conf->CONF["Settings"]["UserSaveSettings"]);
			if (count($UserSaveSettings) == 4) {
				Debug("Session", "SaveUserSettings", "Settings column defined (correctly) in config file", __FILE__, __LINE__, DEBUG_SESS_OK);
				$SettingsDBAlias = $UserSaveSettings[0];
				$SettingsTable = $UserSaveSettings[1];
				$SettingsUserID = $UserSaveSettings[2];
				$SettingsColumn = $UserSaveSettings[3];
				$Query = "UPDATE $SettingsTable SET
									$SettingsColumn = ".NitroPrepareDB(serialize($this->User))."
									WHERE $SettingsUserID = $this->UserID
									";
				if ($this->DB[$SettingsDBAlias]->query($Query)) {
					Debug("Session", "SaveUserSettings", "Settings saved", __FILE__, __LINE__, DEBUG_SESS_OK);
					$RV = TRUE;	
				} else {
					Debug("Session", "SaveUserSettings", "Settings could not be saved", __FILE__, __LINE__, DEBUG_SESS_ERR);
					$RV = FALSE;
				}
			} else {
				Debug("Session", "SaveUserSettings", "Settings column not defined (correctly) in config file", __FILE__, __LINE__, DEBUG_SESS_ERR);
				$RV = FALSE;
			}
		}

		$__NSess = $this; //force write of session to $_SESSION["Nitro"]

		DebugCloseGroup(DEBUG_SESS_OK);

		return $RV;
	}

	function DrawLoginScreen() {
		DebugGroup(__CLASS__, __FUNCTION__, "Drawing login screen", __FILE__, __LINE__, DEBUG_SESS_OK);
		if($this->Conf->CONF["Settings"]["ForceSSL"] && $_SERVER["HTTPS"] != "on" && $_SERVER["HTTP_POUND_HTTPS"] != "on"){
			if (!$Server = $this->Conf->CONF["Settings"]["ForceSSLServer"]) {
				$Server = $_SERVER["HTTP_HOST"];
			}
			Debug(__CLASS__, __FUNCTION__, "SSL forced, but not turned on, redirecting", __FILE__, __LINE__, DEBUG_SESS_OK);
			$this->SaveToDisk();
			Header("Location: https://".$Server.$_SERVER["REQUEST_URI"]);
			exit;
		}
		$DB = $this->DB; global $DB, $GlobalDBAlias; // needed for Template class
		include_once NITRO_PATH.'Template.inc.php';
		$this->LoginSalt = NitroCreateRandomString(32);
		
		$POST = $_POST;
		unset($POST['_Nitro_Login_username']); // remove username/password from POST data
		unset($POST['_Nitro_Login_password']); // prevents repost and filling of POST on password failure

		$Login = new NitroTemplate(GetNitroTemplateID("NitroLogin"));
		$Login->Assign("GET", base64_encode(serialize($_GET)));
		$Login->Assign("POST", base64_encode(serialize($POST)));
		$Login->Assign("username", htmlspecialchars($_REQUEST["_Nitro_Login_username"]));
		$Login->Assign("salt", $this->LoginSalt);
		$Login->Assign("LoggedOn", $this->LoggedOn);
		$Login->Assign("SessionExpired", $this->SessionExpired);
		$Login->Assign("LoginFailed", ($_POST['_Nitro_Login_username'] ? $this->LoginFailed : FALSE));
		
		//TODO: Login No rights implementeren
		$RV = $Login->fetch("file:".NITRO_PATH._DEF_LOGINTEMPLATE);

		DebugCloseGroup(DEBUG_SESS_OK);
		return $RV;
	}

	/**
	 * Clear the page stack set during logon
	 */
	function ClearPageStack()
	{
		$this->PageStack = Array();
	}

	/**
	 * Raise fatal session error, show error page and exit
	 */
	function RaiseFatalError()
	{
		if (file_exists($_SERVER["DOCUMENT_ROOT"]."/SessionError.php")) {
			/* Include error page /SessionError.php if error found and file exists */
			include $_SERVER["DOCUMENT_ROOT"]."/SessionError.php";
		} elseif (file_exists($_SERVER["DOCUMENT_ROOT"]."/SessionError.html")) {
			/* Include error page /SessionError.html if error found and file exists */
			include $_SERVER["DOCUMENT_ROOT"]."/SessionError.html";
		} elseif (file_exists(NITRO_PATH."/Defaults/Texts/SessionError.html")) {
			/* Include error page Defaults/Texts/SessionError.html if error found and file exists */
			include NITRO_PATH."Defaults/Texts/SessionError.html";
		} else {
			echo "ERROR: ".$this->Error;
		}
		echo $this->Error;
		exit;
	}
	
	function SaveToDisk()
	{
		global $__Nitro_ReadOnly_Session;
		DebugGroup(__CLASS__, __FUNCTION__, "Saving session to disk", __FILE__, __LINE__, DEBUG_SESS_OK);
		if ($__Nitro_ReadOnly_Session || Headers_sent()) {
			//Headers sent out, can not restart session to save it;
			// OR session doesnt need to be saved
			$rv = FALSE;
		} else {
			NitroSession_SaveToDisk();
			$rv = TRUE;
		}

		DebugCloseGroup(DEBUG_SESS_OK);
		return $rv;
	}
}
?>
Return current item: OpenNitro