PHP

Mongo DB User session management script v3

<?php

import::load_function('aes_function');
import::load_function('generate_pass');
import::load_function('ip_range_check');

/**
 * Default Session Class
 *
 * This is the default session clas of the framework.
 *
 * @author Sam Millman
 */
class session {

	public $user;

	/**
	 * Constructor
	 */
	function __construct() {

		$this->user = import::instantiate(ROOT.'/application/models/users.php');

		$this->start_session();
	}

	/**
	 * Start the session
	 */
	public function start_session(){

		global $session_expire;

		/** Session in DB logged? */
		if($_SESSION['logged']) {

			/** Only go firther if the cookies are set */
			if(!isset($_COOKIE['_temp'])){
				$this->logout(false);
			}

			/** If session has expired check by cookie */
			if($session_expire < time()){
				if(isset($_COOKIE['_temp'])){
					/** Revert session for saftey before checking cookie */
					$this->session_defaults();
					@$this->_checkCookie();
				}
			}

			/** Check session as normal */
			@$this->_checkSession();

		}elseif(isset($_COOKIE['_perm'])){

			/** User remembered? */
			//$this->session_defaults();
			//$user = Db::getInstance()->getCollection("users")->findOne(array("_id"=> new MongoId(AESDecryptCtr($_COOKIE['_perm']))));

			/** @todo This will not work needs to set session manually */
			//$this->_setSession($user);

			//$_SESSION['AUTH_TIER2'] = false;


		}else{

			/** Else in any other case default session variables */
			@$this->session_defaults();

		}

	}

	/**
	 * Set the default session values
	 */
	function session_defaults() {
		$_SESSION['logged'] = false;
		$_SESSION['uid'] = 0;
		$_SESSION['server_key'] = null;
		$_SESSION['AUTH_TIER2'] = false;
		$_SESSION['email'] = '';
	}

	/**
	 * Check the session
	 */
	function _checkSession() {

		/** Lets for the criteria including the subdocument */
		$criteria['_id'] = new MongoId($_SESSION['uid']);
		$criteria['email'] = $_SESSION['email'];

		/** This is a very niftey little trick. We check for the session existance */
		$criteria['ins.'.session_id()] = array("\$exists"=>true);

		/** Query for the object */
		$user = Db::getInstance()->getCollection("users")->findOne($criteria);

		$ins = $user['ins'][session_id()];

		if(($ins['ip'] == $_SERVER['REMOTE_ADDR']) && ($ins['key'] == $_SESSION['server_key'])){

			/** VALID */
			@$this->_setSession($user);
		}else{

			/** Not VALID */
			@$this->logout(false);
		}
	}

	/**
	 * Set the session
	 *
	 * @param string $user
	 * @param int $remember
	 */
	function _setSession($user, $remember = false) {

		if($user['single_logon']){
			/** Delete all other sessions */
			unset($user['ins']);
		}

		/** GC old sessions Or not? gonna need to choose */

		/** Set session */
		$_SESSION['uid'] = strval($user['_id']);
		$_SESSION['email'] = htmlspecialchars($user['email']);
		$_SESSION['logged'] = true;

		$ins['key'] = $_SESSION['server_key'] = AESEncryptCtr(generate_new_pass());
		$ins['ip'] = $_SERVER['REMOTE_ADDR'];
		$ins['agent'] = $_SERVER['HTTP_USER_AGENT'];
		$ins['gateway'] = $_SERVER['REQUEST_URI'];
		$ins['last_active'] = new MongoDate();
		$ins['ts'] = new MongoDate();

		/** Produces a subdocument of the format - ins: {session_id() : {$key, $ip, $ts}} */
		$user['ins'][session_id()] = $ins;

		/** Populate and save the user */
		$this->user->attributes($user);
		$this->user->save();

		/** Now if the user needs notifying via email lets do it */
		if($user['email_logins']){
			$this->user->email_login();
		}

		@$this->_setCookie($remember, $ins);

	}

	function fb_login($me){

		$this->logout(false);

		/** Find the user */
		$user = Db::getInstance()->getCollection("users")->findOne(array("email" => $me['email'], "FB_ID"=>$me['id']));

		if($user){
			if($this->check_login($user)){
				return array("ERROR"=>false);
			}else{
				return array("ERROR"=>true);
			}
		}else{
			return array("ERROR"=>true, "message"=>"NOT_FOUND");
		}
	}

	function linkedin_login($userid){

		$this->logout(false);

		/** Find the user */
		$user = Db::getInstance()->getCollection("users")->findOne(array("L_ID"=>$userid));

		if($user){
			if($this->check_login($user)){
				return array("ERROR"=>false);
			}else{
				return array("ERROR"=>true);
			}
		}else{
			echo "error";
			return array("ERROR"=>true, "message"=>"NOT_FOUND");
		}

	}

	/**
	 * Log the user in
	 *
	 * @param string $username
	 * @param string $password
	 * @param int $remember
	 */
	public function login($username, $password, $remember = false){

		$this->logout(false);

		/** Hash PW */
		$password = hash("sha256", $password);

		/** Find the user */
		$user = Db::getInstance()->getCollection("users")->findOne(array("email" => $username, "password"=>$password));

		if($user){
			if($this->check_login($user, (bool)$remember)){
				return true;
			}else{
				return array("SILENT_ERROR"=>true);
			}
		}else{
			return false;
		}

	}

	function check_login($data, $remember){

		if(!$this->is_deleted($data)){
			if(!$this->is_banned($data)){

				if($this->is_ip_allowed($data)){

					@log::add(array("userid"=>$data['email'], "success"=>true, "type"=>"LOGIN_ATTEMPT"));

					$this->_setSession($data, $remember);

					$_SESSION['AUTH_TIER2'] = true;
					return true;

				}else{
					flash_message::ERROR("You are not logging from a valid IP");
					return false;
				}
			}else{
				flash_message::ERROR("Your account has been banned");
				return false;
			}
		}else{
			flash_message::ERROR("Your account is pending deletion and will soon be deleted across our network.");
			return false;
		}


	}

	function logout($remember = true){

		setcookie("_temp", "", 1);

		if(!$remember){
			setcookie("_perm", "", 1);
		}

		Db::getInstance()->getCollection("users")->update(array("_id" => new MongoId($_SESSION['uid'])), array("\$unset" => array("ins.".session_id()=>1)), array("safe" => true));

		session_unset();
		$this->session_defaults();
		return true;
	}

	function _setCookie($remember, $ins){

		/*
		 * This will set the cookies for the user so they can be remembered each visit
		 */

		$cookie_string = AESEncryptCtr(strval($this->user->_id));
		$session_cookie = AESEncryptCtr(session_id());
		$key = $ins['key'];

		if($remember){
			setcookie("_perm", $cookie_string, time()+60*60*24*365*10, "/");
		}

		setcookie("_temp", serialize(array($cookie_string, $key)), 0, "/");

	}

	function _checkCookie(){

		if(isset($_COOKIE['_temp'])){

			$cookie = $_COOKIE['_temp'];
			list($user_id, $key) = unserialize($cookie);
			$user_id = AESDecryptCtr($user_id);

			$criteria['_id'] = new MongoId($user_id);
			$criteria['ins.'.session_id()] = array("\$exists"=>true);

			$user = Db::getInstance()->getCollection("users")->findOne($criteria);
			$ins = $user['ins'][session_id()];

			if(($ins['ip'] == $_SERVER['REMOTE_ADDR']) && ($ins['key'] == $key)){

				$_SESSION['AUTH_TIER2'] = true;
				@$this->_setSession($user);

			}else{

				$this->logout(false);
			}
		}
	}

	function is_banned($user){

		if($user['banned']){

			/** Add log entry, log is not fsynced */
			@log::add(array("userid"=>$user['email'], "success"=>false, "why"=>"Banned/Locked", "type"=>"LOGIN_ATTEMPT"));

			/** Log them out completely */
			@$this->logout(false);
			return true;
		}

		return false;
	}

	function is_deleted($user){

		if($user['deleted']){
			return true;
		}

		return false;
	}

	function is_ip_allowed($user){

		$allowed_ips = $user['allowed_ips'];

		/** If the return is valid */
		if(is_array($allowed_ips)){

			/** Is the IP Allowed? */
			if(!isset($allowed_ips[$_SERVER['REMOTE_ADDR']])){

				/** Then is it in a range? */
				foreach($allowed_ips as $k=>$v){

					/** One by one find the range and compare */
					if($v['start_ip'] && $v['end_ip']){
						/** We have touched a range lets check it out */
						$ok = ip_in_range($ip, $v['start_ip']."-".$v['end_ip']);
						if($ok){
							return true;
						}
					}
				}

				/** NO */
				@log::add(array("userid"=>$user['email'], "success"=>false, "why"=>"IP Not Allowed", "type"=>"LOGIN_ATTEMPT"));
				@$this->session_defaults();
				return false;
			}

			return true;
		}

		return true;
	}
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s