Return to Snippet

Revision: 26385
at November 27, 2011 08:45 by ptejada


Updated Code
<?php
// For Support visit http://crusthq.com/projects/uFlex/
// ---------------------------------------------------------------------------
// 	  uFlex - An all in one authentication system PHP class
//    Copyright (C) 2011  Pablo Tejada
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program 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 General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see http://www.gnu.org/licenses/gpl-3.0.html.
// ---------------------------------------------------------------------------

/*Thought the Class Official name is userFlex the object is simply named uFlex*/
class uFlex {
    //Constants
    const debug = true;   //Logs extra bits of errors for developers
    const version = 0.75;
    const salt = "sd5a4"; //IMPORTANT: Please change this value as it will make this copy unique and secured
    //End of constants\\\\
    var $id;        //Signed user ID
    var $sid;       //Current User Session ID
    var $username;  //Signed username
    var $pass;      //Holds the user password hash
    var $signed;    //Boolean, true = user is signed-in
    var $data;      //Holds entire user database row
    var $console;   //Cotainer for errors and reports
    var $log;       //Used for traking errors and reports
    var $confirm;   //Holds the hash for any type of comfirmation
    var $tmp_data;  //Holds the temporary user information during registration
    var $opt = array( //Array of Internal options
        "table_name" => "users",
        "cookie_time" => "+30 days",
        "cookie_name" => "auto",
        "cookie_path" => "/",
        "cookie_host" => false,
        "user_session" => "userData",
        "default_user" => array(
                "username" => "Guest",
                "user_id" => 0,
                "password" => 0,
                "signed" => false
                )
        );
    var $validations = array( //Array for default field validations
            "username" => array(
                    "limit" => "3-15",
                    "regEx" => '/^([a-zA-Z0-9_])+$/'
                    ),
            "password" => array(
                    "limit" => "3-15",
                    "regEx" => false
                    ),
            "email" => array(
                    "limit" => "4-35",
                    "regEx" => '/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$/'
                    )
        );
        
	var $encoder = array(
			"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
			"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z",
			0,2,3,4,5,6,7,8,9
		);
    
    //Array of errors
    var $errorList = array(
				//Database Error while caling register functions
            1   => "New User Registration Failed",
				//Database Error while calling update functions
            2   => "The Changes Could not be made", 
				//Database Error while calling activate function
            3   => "Account could not be activated", 
				//When calling pass_reset and the given email doesn't exist in database
            4   => "We don't have an account with this email", 
				//When calling new_pass, the confirmation hash did not match the one in database
            5   => "Password could not be changed. The request can't be validated", 
            6   => "Logging with cookies failed",
            7   => "No Username or Password provided",
            8   => "Your Account has not been Activated. Check your Email for instructions",
            9   => "Your account has been deactivated. Please contact Administrator",
            10  => "Wrong Username or Password",
				//When calling check_hash with invalid hash
            11  => "Confirmation hash is invalid", 
				//Calling check_hash hash failed database match test
            12  => "Your identification could not be confirmed", 
				//When saving hash to database fails
            13  => "Failed to save confirmation request", 
            14 	=> "You need to reset your password to login"
        );
        
		
/** EDITS BELOW THIS LINE WILL NOT BE KEPT WHEN UPDATING CLASS USING THE UPDATER SCRIPT **/
	
	/**
	 * Public function to initiate a login request at any time
	 * 
	 * @access public
	 * @param string $user username or email 
	 * @param string $pass password 
	 * @param bool|int $auto boolean to remember or not the user 
	 */
	function login($user=false,$pass=false,$auto=false){
		//reconstruct object
		self::__construct($user,$pass,$auto);
	}

///////////////////////////////////////////////////////////////////////////////////////////////////////
/*
Register A New User
-Takes two parameters, the first being required
	@info = array object (takes an associatve array, 
				the index being the fieldname(column in database) 
				and the value its content(value)
	+optional second parameter
	@activation = boolean(true/false)
		default = false 
Returns activation hash if second parameter @activation is true
Returns true if second parameter @activation is false
Returns false on Error
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////
    function register($info,$activation = false){
        $this->logger("registration"); //Index for Errors and Reports

        //Saves Registration Data in Class
        $this->tmp_data = $info;

        //Validate All Fields
        if(!$this->validateAll()) return false; //There are validations error

        //Built in actions for special fields
        //Hash Password
        if(isset($info['password'])){
            $this->hash_pass($info['password']);
            $info['password'] = $this->pass;
        }
        //Check for Email in database
        if(isset($info['email']))
            if($this->check_field('email',$info['email'],"This Email is Already in Use"))
                return false;

        //Check for username in database
        if(isset($info['username']))
            if($this->check_field('username',$info['username'], "This Username is not available"))
                return false;

        //Check for errors
        if($this->has_error()) return false;

        //Set Registration Date
        $info['reg_date'] = time();

        //User Activation
        if(!$activation){ //Activates user upon registration
            $info['activated'] = 1;
        }

        //Prepare Info for SQL Insertion
        foreach($info as $index => $val){
            if(!preg_match("/2$/",$index)){ //Skips double fields
                $into[] = $index;
                $values[] = "'".mysql_real_escape_string($val)."'";
            }
        }

        $into = implode(", ",$into);
        $values = implode(",",$values);

        //Prepare New User	Query
        $sql = "INSERT INTO {$this->opt['table_name']} ($into) 
					VALUES($values)";
        //exit($sql);

        //Enter New user to Database
        if($this->check_sql($sql)){
            $this->report("New User \"{$info['username']}\" has been registered");
            $this->id = mysql_insert_id();
            if($activation){
                //Insert Validation Hash
                $this->make_hash($this->id);
                $this->save_hash();
                return $this->confirm;
            }
            return true;
        }else{
            $this->error(1);
            return false;
        }
    }

///////////////////////////////////////////////////////////////////////////////////////////////////////
/*
Similar to the register method function in structure
This Method validates and updates any field in the database
-Takes one parameter
	@info = array object (takes an associatve array, 
				the index being the fieldname(column in database) 
				and the value its content(value)
On Success returns true
On Failure return false	
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////
    function update($info){
        $this->logger("update"); //Index for Errors and Reports

        //Saves Updates Data in Class
        $this->tmp_data = $info;

        //Validate All Fields
        if(!$this->validateAll())
            return false; //There are validations error

        //Built in actions for special fields
        //Hash Password
        if(isset($info['password'])){
            $this->hash_pass($info['password']);
            $info['password'] = $this->pass;
        }
        //Check for Email in database
        if(isset($info['email']))
            if($this->check_field('email',$info['email'],"This Email is Already in Use"))
                return false;

        //Check for errors
        if($this->has_error()) return false;

        //Prepare Info for SQL Insertion
        foreach($info as $index => $val){
            if(!preg_match("/2$/",$index)){ //Skips double fields
                $value = "'".mysql_real_escape_string($val)."'";
                $set[] = "{$index}={$value}";
            }
        }

        $set = implode(", ",$set);

        //Prepare User Update	Query
        $sql = "UPDATE {$this->opt['table_name']} SET $set 
					WHERE user_id='{$this->id}'";
        //exit($sql);

        //Check for Changes
        if($this->check_sql($sql)){
            $this->report("Information Updated");
            $_SESSION['uFlex']['update'] = true;
            return true;
        }else{
            $this->error(2);
            return false;
        }
    }

///////////////////////////////////////////////////////////////////////////////////////////////////////
/*
Adds validation to queue for either the Registration or Update Method
Single Entry:
	Requires the first two parameters
		@name  = string (name of the field to be validated)
		@limit = string (range in the format of "5-10")
			*to make a field optional start with 0 (Ex. "0-10")
	Optional third paramenter
		@regEx = string (Regular Expresion to test the field)
_____________________________________________________________________________________________________

Multiple Entry:
	Takes only the first argument
		@name = Array Object (takes an object in the following format:
			array(
				"username" => array(
						"limit" => "3-15",
						"regEx" => "/^([a-zA-Z0-9_])+$/"
						),
				"password" => array(
						"limit" => "3-15",
						"regEx" => false
						)
				);
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////
    function addValidation($name,$limit = "0-1",$regEx = false){
        $this->logger("registration");
        if(is_array($name)){
            if(!is_array($this->validations))
                $this->validations = array(); //If is not an array yet, make it one
            $new = array_merge($this->validations,$name);
            $this->validations = $new;
            $this->report("New Validation Object added");
        }else{
            $this->validations[$name]['limit'] = $limit;
            $this->validations[$name]['regEx'] = $regEx;
            $this->report("The $name field has been added for validation");
        }
    }

///////////////////////////////////////////////////////////////////////////////////////////////////////	
/*
Activates Account with hash
Takes Only and Only the URL parameter of the confirmation page
	@hash = string
Returns true on account activation and false on failure
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////
    function activate($hash){
        $this->logger("activation");

        if(!$this->check_hash($hash)) return false;

        $sql = "UPDATE {$this->opt['table_name']} SET activated=1, confirmation='' WHERE confirmation='{$hash}' AND user_id='{$this->id}'";

        if($this->check_sql($sql)){
            $this->report("Account has been Activated");
            return true;
        }else{
            $this->error(3);
            return false;
        }
    }

///////////////////////////////////////////////////////////////////////////////////////////////////////
/*
Method to reset password, Returns confirmation code to reset password
-Takes one parameter and is required
	@email = string(user email to reset password)
On Success it returns an array(email,username,user_id,hash) which could then be use to 
 construct the confirmation URL and Email
On Failure it returns false
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////
    function pass_reset($email){
        $this->logger("pass_reset");
        $sql = "SELECT * FROM {$this->opt['table_name']} WHERE email='{$email}'";

        $user = $this->getRow($sql);

        if($user){
        	if(!$user['activated'] and !$user['confirmation']){
        		//The Account has been manually disabled and can't reset password
        		$this->error(9);
				return false;
        	}
        	
            $this->make_hash($user['user_id']);
            $this->id = $user['user_id'];
            $this->save_hash();

            $data = array(
            	"email" => $email,
            	"username" => $user['username'],
            	"user_id" => $user['user_id'],
                "hash" => $this->confirm
			);
            return $data;
        }else{
            $this->error(4);
            return false;
        }
    }

///////////////////////////////////////////////////////////////////////////////////////////////////////
/*
Changes a Password with a Confirmation hash from the pass_reset method
*this is for users that forget their passwords to change the signed user password use ->update()
-Takes two parameters
	@hash = string (pass_reset method hash)
	@new = array (an array with indexes 'password' and 'password2')
					Example:
					array(
						[password] => pass123
						[password2] => pass123
					)
					*use ->addValidation('password', ...) to validate password
Returns true on a successful password change
Returns false on error
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////
    function new_pass($hash,$newPass){
        $this->logger("new_pass");

        if(!$this->check_hash($hash)) return false;
        
        $this->tmp_data = $newPass;
        if(!$this->validateAll()) return false; //There are validations error

        $pass = $this->hash_pass($newPass['password']);

        $sql = "UPDATE {$this->opt['table_name']} SET password='{$pass}', confirmation='', activated=1 WHERE confirmation='{$hash}' AND user_id='{$this->id}'";
        if($this->check_sql($sql)){
            $this->report("Password has been changed");
            return true;
        }else{
            //Error
            $this->error(5);
            return false;
        }
    }

 /*////////////////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*\
////////Private and Secondary Methods below this line\\\\\\\\\\\\\
 \*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/////////////////////////////////////////////*/
/*Object Constructor*/
    function __construct($user = false,$pass = false,$auto = false){
        $this->logger("login"); //Index for Reports and Errors;
        
        if(!isset($_SESSION) and !headers_sent()){
	        session_start();
			$this->report("Session is been started...");
        }elseif(isset($_SESSION)){
        	$this->report("Session has already been started");
        }else{
        	$this->error("Session could not be started");
        	return; //Finish Execution  	
        }
		
        $this->sid = session_id();

        $result = $this->loginUser($user,$pass,$auto);
        if($result == false){
        	$this->session($this->opt['default_user']);
            $this->update_from_session();
            $this->report("User is " + $this->username);
        }else{
            if(!$auto and isset($_SESSION['uFlex']['remember'])){
                unset($_SESSION['uFlex']['remember']);
                $this->setCookie();
            }
        }
        return true;
    }
	
	/**
	 * Private Login proccesor function
	 * 
	 */
    private function loginUser($user = false,$pass = false,$auto = false){
        //Session Login
        if($this->session("signed")){
            $this->report("User Is signed in from session");
            $this->update_from_session();
            if(isset($_SESSION['uFlex']['update'])){
                $this->report("Updating Session from database");
                //Get User From database because its info has change during current session
                $update = $this->getRow("SELECT * FROM {$this->opt['table_name']} WHERE user_id='{$this->id}'");
                $this->update_session($update);
                $this->log_login(); //Update last_login
            }
            return true;
        }
        //Cookies Login
        if(isset($_COOKIE[$this->opt['cookie_name']]) and !$user and !$pass){
            $c = $_COOKIE[$this->opt['cookie_name']];
            $this->report("Attemping Login with cookies");
            if($this->check_hash($c,true)){
                $auto = true;
                $cond = "username='{$this->username}'";
            }else{
                $this->error(6);
                $this->logout();
                return false;
            }
        }else{
        //Credentials Login
            if($user && $pass){
                if(preg_match($this->validations['email']['regEx'],$user)){
                    //Login using email
                    $cond = "email='{$user}'";
                }else{
                    //Login using username
                    $cond = "username='{$user}'";
                    
                }
                $this->hash_pass($pass);
                $this->report("Credentials received");
            }else{
                $this->error(7);
                return false;
            }
        }

        $this->report("Querying Database to authenticate user");
        //Query Database and check login
        $sql = "SELECT * FROM {$this->opt['table_name']} WHERE {$cond} AND password='{$this->pass}'";
        $userFile = $this->getRow($sql);
        if($userFile){
            //If Account is not Activated
            if($userFile['activated'] == 0){
                if($userFile['last_login'] == 0){
                    //Account has not been activated
                    $this->error(8);
                }else if(!$userFile['confirmation']){
                    //Account has been deactivated
                    $this->error(9);
                }else{
                	//Account deativated due to a password reset or reactivation request
                	$this->error(14);
                }
                return false;
            }
            //Account is Activated and user is logged in
            $this->update_session($userFile);

            //If auto Remember User
            if($auto){
                $this->setCookie();
            }
            $this->log_login(); //Update last_login
            //Done
            $this->report("User Logged in Successfully");
            return true;
        }else{
            if(isset($_COOKIE[$this->opt['cookie_name']])){
                $this->logout();
            }
            $this->error(10);
            return false;
        }
    }

    function logout(){
        $this->logger("login");
        $deleted = setcookie($this->opt['cookie_name'],"",time() - 3600,"/"); //Deletes the Auto Coookie
        $this->signed = 0;
		//Import default user object
		$this->data = $_SESSION[$this->opt['user_session']] = $this->opt['default_user'];
        if(!$deleted){
            $this->report("The Autologin cookie could not be deleted");
        }
        $this->report("User Logged out");
    }

    private function log_login(){
        //Update last_login
        $time = time();
        $sql = "UPDATE {$this->opt['table_name']} SET last_login='{$time}' WHERE user_id='{$this->id}'";
        if($this->check_sql($sql))
            $this->report("Last Login updated");
    }

    function setCookie(){
        if($this->pass and $this->id){

            $code = $this->make_hash($this->id,$this->pass);

            if(!headers_sent()){
                //echo "PHP";
                setcookie($this->opt['cookie_name'],$code,strtotime($this->opt['cookie_time']),
                    $this->opt['cookie_path'],$this->opt['cookie_host']);
            }else{
                //Headers have been sent use JavaScript to set cookie
                $time = intval($this->opt['cookie_time']);
                echo "<script>";
                echo '
		          function setCookie(c_name,value,expiredays){
		            var exdate=new Date();
		            exdate.setDate(exdate.getDate()+expiredays);
		            document.cookie=c_name+ "=" +escape(value)+((expiredays==null) ? "" : "; expires="+exdate.toUTCString()); path=escape("'.
		                    $this->opt["cookie_path"].'");
		          }
		        ';
                echo "setCookie('{$this->opt['cookie_name']}','{$code}',{$time})";
                echo "</script>";
            }

            $this->report("Cookies have been updated for auto login");
        }else{
            $this->error("Info required to set the cookie {$this->opt['cookie_name']} is not available");
        }
    }
	
	private function session($index=false, $val=false){
		if(is_string($index) and !$val){
			return @$_SESSION[$this->opt['user_session']][$index];
		}
		
		if(is_string($index) and $val){
			$_SESSION[$this->opt['user_session']][$index] = $val;
			return;
		}
		
		if(is_array($index) and !$val){
			$_SESSION[$this->opt['user_session']] = $index;
			return;	
		}
		//return full session user data
		return $_SESSION[$this->opt['user_session']];	
	}
	
    private function update_session($d){
        unset($_SESSION['uFlex']['update']);

        $this->session($d);
		$this->session("signed",1);

        $this->report("Session updated");
        $this->update_from_session();
    }

    private function update_from_session(){
        $d = $this->session();

        $this->id = $d['user_id'];
        $this->data = $d;
        $this->username = $d['username'];
        $this->pass = $d['password'];
        $this->signed = $d['signed'];

        $this->report("Session has been imported to the object");
    }

    function hash_pass($pass){
        $salt = uFlex::salt;
        $this->pass = md5($salt.$pass.$salt);
        return $this->pass;
    }

    function logger($log){
        $this->log = $log;
        unset($this->console['errors'][$log]);
        unset($this->console['form'][$log]);
        $this->report(">>Startting new $log request");
    }

    function report($str = false){
        $index = $this->log;
        if($str){
            $str = ucfirst($str);
            $this->console['reports'][$index][] = $str; //Strore Report
        }else{
            if($index){
                return $this->console['reports'][$index]; //Return the $index Reports Array
            }else{
                return $this->console['reports']; //Return the Full Reports Array
            }
        }
    }

    function error($str = false){
        $index = $this->log;
        if($str){
            $err = is_int($str) ? $this->errorList[$str] : $str;
            $this->console['errors'][$index][] = $err; //Store Error
            if(is_int($str)){
            	$this->report("Error[{$str}]: {$err}"); //Report The error
			}else{
            	$this->report("Error: {$str}"); //Report The error				
			}
        }else{
            if($index){
                if(!isset($this->console['errors'][$index]))
                    return false;
                return $this->console['errors'][$index]; //Return the $index Errors Array
            }else{
                return $this->console['errors']; //Return the Full Error Array
            }
        }
    }

    //Adds fields with errors to the console
    function form_error($field = false,$error = false){
        $index = $this->log;
        if($field){
            if($error){
                $this->console['form'][$index][$field] = $error;
                $this->error($error);
            }else{
                $this->console['form'][$index][] = $field;
            }
        }else{
            if($index){
                if(!isset($this->console['form'][$index]))
                    return false;
                return $this->console['form'][$index]; //Return the $index Errors Array
            }else{
                return $this->console['form']; //Return the Full form Array
            }
        }
    }

    //Check for errors in the console
    function has_error($index = false){
        //Check for errors
        $index = $index?$index:$this->log;
        $count = @count($this->console['errors'][$index]);
        if($count){
            $this->report("$count Error(s) Found!");
            return true;
        }else{
            $this->report("No Error Found!");
            return false;
        }
    }

    //Generates a unique comfirm hash
    function make_hash($uid,$hash = false){
        $e_uid = $this->encode($uid);
        $e_uid_length = strlen($e_uid);
        $e_uid_length = str_pad($e_uid_length,2,0,STR_PAD_LEFT);
        $e_uid_pos = rand(10,32 - $e_uid_length - 1);

        if(!$hash){
            $hash = md5(uniqid(rand(),true));
        }
        //$code = substr($code, 0, $length);
        $code = $e_uid_pos.$e_uid_length;
        $code .= substr($hash,0,$e_uid_pos - strlen($code));
        $code .= $e_uid;
        $code .= substr($hash,strlen($code));

        $this->confirm = $code;
        return $code;
    }

    //Validates a confirmation hash
    function check_hash($hash,$bypass = false){
        if(strlen($hash) != 32 || !preg_match("/^[0-9]{4}/",$hash)){
            $this->error(11);
            return;
        }

        $e_uid_pos = substr($hash,0,2);
        $e_uid_length = substr($hash,2,2);
        $e_uid = substr($hash,$e_uid_pos,$e_uid_length);

        $uid = $this->decode($e_uid);

        $sql = "SELECT * FROM {$this->opt['table_name']} WHERE user_id={$uid} AND confirmation='{$hash}'";

        //Bypass hash confirmation and get the user by partially matching its password
        if($bypass){
            preg_match("/^([0-9]{4})(.{2,".($e_uid_pos - 4)."})(".$e_uid.")/",$hash,$exerpt);
            $pass = $exerpt[2];
            $sql = "SELECT * FROM {$this->opt['table_name']} WHERE user_id={$uid} AND password LIKE '{$pass}%'";
        }

        $result = $this->getRow($sql);

        if(!$result){
            $this->report("The user ID and the confirmation hash did not match");
            $this->error(12);
            return false;
        }
        if($this->signed and $this->id == $result['user_id']){
            $this->logout();
        }

        //Hash is valid import user's info to object
        $this->data = $result;
        $this->id = $result['user_id'];
        $this->username = $result['username'];
        $this->pass = $result['password'];

        $this->report("Hash successfully validated");
        return true;
    }

    //Saves the confirmation hash in the database
    function save_hash(){
        if($this->confirm and $this->id){
            $sql = "UPDATE {$this->opt['table_name']} SET confirmation='{$this->confirm}', activated=0 WHERE user_id='{$this->id}'";
            if(!$this->check_sql($sql)){
                $this->error(13);
                return false;
            }else{
                $this->report("Confirmation hash has been saved");
            }
        }else{
            $this->report("Can't save Confirmation hash");
            return false;
        }
        return true;
    }
	
    //Test field in database for a value
    function check_field($field,$val,$err = false){
        $query = mysql_query("SELECT {$field} FROM {$this->opt['table_name']} WHERE {$field}='{$val}' ");
        if(mysql_num_rows($query) >= 1){
            if($err){
                $this->form_error($field,$err);
            }else{
                $this->form_error($field,"The $field $val exists in database");
            }
            $this->report("There was a match for $field = $val");
            return true;
        }else{
            $this->report("No Match for $field = $val");
            return false;
        }
    }

    //Executes SQL query and checks for success
    function check_sql($sql,$debug = false){
        $this->report("SQL: {$sql}"); //Log the SQL Query
        if(!mysql_query($sql)){
            if(self::debug){
                $this->error(mysql_error());
            }
            return false;            
        }else{
            $rows = mysql_affected_rows();
            if($rows > 0){
                //Good, Rows where affected
                $this->report("$rows row(s) where Affected");
                return true;
            }else{
                //Bad, No Rows where Affected
                $this->report("No rows were Affected");
                return false;
            }
        }
    }

    //Executes SQL query and returns an associate array of results
    function getRow($sql){
        $this->report("SQL: {$sql}"); //Log the SQL Query first
        $query = mysql_query($sql);
        if(mysql_error() and self::debug){
            $this->error(mysql_error());
        }

        if(@mysql_num_rows($query)){
            while($row = mysql_fetch_array($query,MYSQL_ASSOC)){
                $rows[] = $row;
            }
        }else{
            $this->report("Query returned empty");
            return false;
        }
        return $rows[0];
    }


    //Validates All fields in ->tmp_data array
    private function validateAll(){
        $info = $this->tmp_data;
        foreach($info as $field => $val){
            //Match double fields
            if(isset($info[$field.(2)])){
                if($val != $info[$field.(2)]){
                    $this->form_error($field, ucfirst($field) . "s did not match");
                    //return false;
                }else{
                    $this->report(ucfirst($field) . "s matched");
                }
            }

            $this->tmp_data[$field] = trim($val); //Trim white spaces at end and start

            //Validate field
            if(!isset($this->validations[$field]))
                continue;
            $opt = $this->validations[$field];
            $this->validate($field,$opt['limit'],$opt['regEx']);
        }
        return $this->has_error() ? false : true;
    } 

    //Validates field($name) in tmp_data
    private function validate($name,$limit,$regEx = false){
        $Name = ucfirst($name);
        $str = $this->tmp_data[$name];
        $l = explode("-",$limit);
        $min = intval($l[0]);
        $max = intval($l[1]);
        if(!$max and !$min){
            $this->error("Invalid second paramater for the $name validation");
            return false;
        }
        if(!$str){
            if(!isset($this->tmp_data[$name])){
                $this->report("missing index $name from the POST array");
            }
            if(strlen($str) == $min){
                $this->report("$Name is blank and optional - skipped");
                return true;
            }
            $this->form_error($name,"$Name is required.");
            return false;
        }
        if(strlen($str) > $max){
            $this->form_error($name,"The $Name is larger than $max characters.");
            return false;
        }
        if(strlen($str) < $min){
            $this->form_error($name,"The $Name is too short. it should at least be $min characters long");
            return false;
        }
        if($regEx){
            preg_match_all($regEx,$str,$match);
            if(count($match[0]) != 1){
                $this->form_error($name,"The $Name \"{$str}\" is not valid");
                return false;
            }
        }

        $this->report("The $name is Valid");
        return true;
    }

	//Encoder
	function encode($d){
		$k=$this->encoder;preg_match_all("/[1-9][0-9]|[0-9]/",$d,$a);$n="";$o=count($k);foreach($a[0]as$i){if($i<$o){
			$n.=$k[$i];}else{$n.="1".$k[$i-$o];}}
		return $n;
	}
	//Decoder
	function decode($d){
		$k=$this->encoder;preg_match_all("/[1][a-zA-Z]|[2-9]|[a-zA-Z]|[0]/",$d,$a);$n="";$o=count($k);foreach($a[0]as$i){
			$f=preg_match("/1([a-zA-Z])/",$i,$v);if($f==true){	$i=$o+array_search($v[1],$k);}else{$i=array_search($i,$k);}$n.=$i;}
		return $n;
	}	
}
?>

Revision: 26384
at April 26, 2010 16:45 by ptejada


Initial Code
<?php
// ---------------------------------------------------------------------------
//    uFlex - An all in one authentication system PHP class
//    Copyright (C) 2010  Pablo Tejada
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program 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 General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see http://www.gnu.org/licenses/gpl-3.0.html.
// --------------------------------------------------------------------------- 
// V 0.10 - Last modified 4/26/2010
//  +Registration Method
//  	-Custome and Built-in fields validation
//  	-Extendable: add as many fields and validation as required
//  	-Built-in Redundancy check for email and username
//  	-Built-in account activation by email
//  +Update Method to update anyfield on database
//  	-Built-in Redundancy check for email
//  	-Custome and Built-in fields validation
//  +Automatic user session handler
//  	-Remember user with cookies
//  	-Handles sessions on new object
//  +Class wide console
//  	-track and log Errors
//  	-Report every steps for each method
//  	-log validations, connection, SQL queries etc...
// ---------------------------------------------------------------------------

/*Thought the Class Official name is userFlex the object is simply named uFlex*/
class uFlex{
	var $salt = "sd5a4"; //IMPORTANT: Please change this value as it will make this copy unique and secured
	var $id;
	var $username;
	var $pass;
	var $signed;
	var $data;
	var $console;
	var $log;
	var $confirm;	
	var $tmp_data;	
	var $validations;

///////////////////////////////////////////////////////////////////////////////////////////////////////
/*
Register A New User
-Takes two parameter the firs been required
	@info = array object (takes an associatve array, 
				the index being the fieldname(column in database) 
				and the value its content(value)
	+optional second parameter
	@activation = boolean(true/false)
		default = false 
Returns activation hash if second parameter @activation is true
Returns true if second parameter @activation is false
Returns false on Error
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////
	function register($info,$activation=false){
		$this->log = "registration";  //Index for Errors and Reports
		
		//Saves Registration Data in Class
		$this->tmp_data = $info;
		
		//Validate Fields Submited Fields
		$validation = array(
			"username" => array(
								"limit" => "3-15",
								"regEx" => "/^([a-zA-Z0-9_])+$/"
								),
			"password" => array(
								"limit" => "3-15",
								"regEx" => false
								),
			"email" => array(
								"limit" => "4-45",
								"regEx" => "/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$/",
								"optional" => true
								)
			);
		//Add Built in Validation to the Array
		$this->addValidation($validation);
		
		//Validate All Fields in the validations array
		foreach($this->validations as $field=>$opt){
			$this->validate($field,$opt['limit'],$opt['regEx']);
		}
		//Check for errors
		if($this->has_error()) return false;
		
	//Built in actions for special fields
		//Hash Password
		if(isset($info['password'])){ 
			$this->hash_pass($info['password']);
			$info['password'] = $this->pass;
		}		
		//Check for Email in database
		if(isset($info['email'])){
			if($this->check_field('email',$info['email'],"This Email is Already in Use")){
				$this->form_error('email');
			}
		}
		//Check for username in database
		if(isset($info['username'])){
			if($this->check_field('username',$info['username'],"This Username is not available")){
				$this->form_error('username');
			}
		}
		
		//Check for errors
		if($this->has_error()) return false;
		
		//Updates $Info, add defaults, and clean left overs
		$info['password'] = $this->pass;
		$info['confirmation'] = $this->confirm;
		$info['reg_date'] = time();
		
		//Generates the Confirmation Code
		$this->uConfirm();
		
		//Activates user upon registration if there is not an activation method
		if(!$activation){
			$info['activated'] = 1;
		}
		
		//Prepare Info for SQL Insertion
		foreach($info as $index => $val){
			if(!preg_match("/2$/",$index)){ //Skips double fields
				$into[] = $index;
				$values[] = "'" . mysql_real_escape_string($val) . "'";
			}
		}		
		
		$into = implode(", ",$into);
		$values = implode(",",$values);
		
		//Prepare New User	Query
		$sql = "INSERT INTO users ($into) 
					VALUES($values)";
		//exit($sql);
		
		//Enter New user to Database
		if($this->check_sql($sql,true) ){
			$this->report("New User \"{$info['username']}\" has been registered");
			if($activation) return "{$this->confirm}:{$this->tmp_data['username']}";
			return true;
		}else{
			$this->error("New User Registration Failed");
			return false;
		}
	}

///////////////////////////////////////////////////////////////////////////////////////////////////////
/*
Similar to the register method function in structure
This Method validates and updates any field in the database
-Takes one parameter
	@info = array object (takes an associatve array, 
				the index being the fieldname(column in database) 
				and the value its content(value)
On Success returns true
On Failure return false	
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////
	function update($info){
		$this->log = "update";  //Index for Errors and Reports
		
		//Saves Updates Data in Class
		$this->tmp_data = $info;
		
		//Check if there have being Changes
		foreach($info as $index=>$val){
			if($this->data[$index] == $val){
				$this->error("{$index} is the same. no changes were made");
				return false;
			}elseif(isset($info[$index.(2)])){
				//Check for equal fields
				if($info[$index] != $info[$index.(2)]){
					$this->error("{$index}s did not match");
					return false;
				}else{
					$this->report("{$index}s match");
				}
			}
		}
		
		//Defaults or Built in Validations
		$validation = array(
			"username" => array(
								"limit" => "3-15",
								"regEx" => "/^([a-zA-Z0-9_])+$/"
								),
			"password" => array(
								"limit" => "3-15",
								"regEx" => false
								),
			"email" => array(
								"limit" => "4-45",
								"regEx" => "/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$/",
								"optional" => true
								)
			);
		//Add Built in Validation to the quene
		$this->addValidation($validation);
		
		//Validate All Fields in the info array with the validation array
		foreach($this->validations as $field=>$opt){
			if(isset($info[$field])){
				$this->validate($field,$opt['limit'],$opt['regEx']);
			}
		}
		//Check for errors
		if($this->has_error()) return false;
		
	//Built in actions for special fields
		//Hash Password
		if(isset($info['password'])){ 
			$this->hash_pass($info['password']);
			$info['password'] = $this->pass;
		}		
		//Check for Email in database
		if(isset($info['email'])){
			if($this->check_field('email',$info['email'],"This Email is Already in Use")){
				$this->form_error('email');
			}
		}
		
		//Check for errors
		if($this->has_error()) return false;
				
		//Prepare Info for SQL Insertion
		foreach($info as $index => $val){
			if(!preg_match("/2$/",$index)){ //Skips double fields
				$value = "'".mysql_real_escape_string($val)."'";
				$set[] = "{$index}={$value}";
			}			
		}		
		
		$set = implode(", ",$set);
		
		//Prepare User Update	Query
		$sql = "UPDATE users SET $set 
					WHERE user_id='{$this->id}'";		
		//exit($sql);
		
		//Check for Changes
		if($this->check_sql_change($sql,true) ){
			$this->report("Information Updated");
			$_SESSION['updated'] = true;
			return true;
		}else{
			$this->error("The Changes Could not be made");
			return false;
		}
	}

///////////////////////////////////////////////////////////////////////////////////////////////////////
/*
Adds validation to quene list for either the Registration or Update Method
Single Entry:
	Requires the first two parameters
		@name = string (name of the field to be validated)
		@limit    = string (range in the format of "5-10")
			*to make a field optional start with 0 (Ex. "0-10")
	Optional third paramenter
		@regEx = string (Regular Expresion to test the field)
Multiple Entry:
	Takes only the first argument
		@name = Array Object (takes an object in the following format:
			array(
				"username" => array(
									"limit" => "3-15",
									"regEx" => "/^([a-zA-Z0-9_])+$/"
									),
				"password" => array(
									"limit" => "3-15",
									"regEx" => false
									)
				);
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////
	function addValidation($name,$limit=false,$regEx=false){
		$this->log = "registration";
		if(is_array($name)){
			if(!is_array($this->validations)) $this->validations = array(); //If is not an array yet, make it one
			$new = array_merge($this->validations,$name);
			$this->validations = $new;
			$this->report("New Validation Object added");
		}else{
			$this->validations[$name]['limit'] = $limit;
			$this->validations[$name]['regEx'] = $regEx;
			$this->report("The $name field has been added for validation");
		}
	}
	
///////////////////////////////////////////////////////////////////////////////////////////////////////	
/*
Activates Account with hash
Takes Only and Only the URL c parameter of the comfirmation page
	@hash = string
Returns true on account activation and false on failure
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////
	function activate($hash){
		$d = explode(":",$hash);
		$this->confirm = $d[0];
		$this->username = $d[1];		
		$sql = "UPDATE users SET activated=1 WHERE confirmation='{$d[0]}' AND username='{$d[1]}'";
		
		if($this->check_sql_change($sql,true)){
			$this->report("Account has been Activated");
			return true;
		}else{
			$this->error("Account could not be activated");
			return false;
		}
	}

///////////////////////////////////////////////////////////////////////////////////////////////////////
/*
Method to reset password, sents an email with a confirmation code to reset password
-Takes one parameter and is required
	@email = string(user email to reset password)
On Success it returns a hash which could then be use to construct the confimration URL
On Failure it returns false
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////
	function pass_reset($email){
		$this->log = "pass_reset";
		$this->uConfirm();
		$sql = "SELECT username,user_id FROM users WHERE email='{$email}'";
		$this->
		$query = mysql_query($sql);
		if(!$query){
			$this->error(mysql_error()); 
			return false;
		}
		$row = mysql_fetch_assoc($query);
		if(count($row) > 1){
			//Send Email
			$code = $this->confirm.":{$row['user_id']}";
			return $code;
		}else{
			$this->error("We don't have an account with this email");
			return false;
		}
	}

///////////////////////////////////////////////////////////////////////////////////////////////////////
/*
Reset a Password with a Confirmation hash from the pass_reset method	
-Takes two parameters
	@hash = string (pass_reset method hash)
	@new  = string (New password)
					*Make sure to validate and comfirm new password in javascript for now
Returns true on a successfull password change
Returns false on error
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////
	function new_pass($hash,$new){
		$d = explode(":",$hash);
		$this->confirm = $d[0];
		$this->id = $d[1];
		$pass = $this->hash_pass($new);
		$sql = "UPDATE users SET password='{$pass}' WHERE confirmation='{$d[0]}' AND user_id='{$d[1]}'";
		
		if($this->check_sql($sql,true)){
			$this->report("Your Password has been Changed");
			return true;
		}else{
			$this->error("Password could not be changed");
			return false;
		}
	}
	
////////Private and Secondary Methods below this line\\\\\\\\\\\\\
/*Star up function*/
	function uFlex($user=false,$pass=false,$auto=false){
		$this->log = "login";  //Index for Reports and Errors;
		session_start();	
		//$this->username = $user;
		//$this->pass = $pass;
		
		$result = $this->login($user,$pass,$auto);
		if($result == false){
			$this->id = 0;
			$this->username = "Guess";
			$this->pass = "";
			$this->signed = false;
			$_SESSION = array("username" => "Guess",
							  "user_id" => 0,
							  "signed" => false
							  );
			$this->report("User is Guess");
		}else{
			
		}
		return true;
	}
	
	private function login($user=false,$pass=false,$auto=false){
		$this->log = "login";  //Index for Reports and Errors;
		//Session Login
		if(@$_SESSION['signed']){
			$this->report("User Is signed in from session");
			$this->update_from_session();
			if(isset($this->data['updated'])){
				//Get User From database because its info has change during current session
				$update = mysql_fetch_assoc(mysql_query("SELECT * FROM users WHERE user_id='{$this->id}'"));
				$this->update_session($update);
				unset($_SESSION['updated']);
				$this->report("Session Updated From Database");
			}
			return true;
		}		
		if(isset($_COOKIE['auto'])){
		//Cookies Login	
			$c = $_COOKIE['auto'];
			$c = explode(":",$c);  //passHash:id => asd5453asf54a:52
			//if($c[1] != 1){ return false; $this->report("No auto Login"); } //No AutoLogin set return false
			$this->id = $c[1];
			$this->pass = $c[0];
			$auto = true;
			$this->report("Attemping Login with cookies");
		}else{
		//Credetials Login
			if(!$user == false && !$pass == false){
				$this->username = $user;
				$this->hash_pass($pass);
				$this->report("Creadentials recieved");
			}else{
				$this->error("No Username or Password provided");
				return false;
			}
		}

		$this->report("I got info Quering Database to autenticate user");
		//Query Database and check login
		$query = mysql_query("SELECT * FROM users WHERE user_id='{$this->id}' OR username='{$this->username}' AND password='{$this->pass}'");
		if(mysql_num_rows($query) == 1){
			$this->data = mysql_fetch_assoc($query);
			$d = $this->data;
			//If Account is not Activated
			if($d['activated'] != 1){
				if($d['last_login'] == 0){
					//Account has not been activated
					$this->error("Your Account has not been Activated. Check your Email for instructions");
				}else{
					//Account has been deactivated
					$this->error("Your account has been deactivated. Please contact Administrator");
				}
				return false;
			}
			//Account is Activated and user is logged in
			$this->update_session($d);
			
			//If auto Remember User
			if($auto){
				$this->setCookie();
			}
			//Update last_login
			$time = time();
			$sql = "UPDATE users SET last_login='{$time}' WHERE user_id='{$this->id}'";
			$this->check_sql($sql,true);
			//Done
			$this->report("User Logged in Successfully");
			return true;
		}else{
			$this->error("Wrong Username or Password");
			return false;
		}
	}
	
	function logout(){
		$this->log = "login";
		setcookie("auto", "", time()-3600,"/",".".$_SERVER['HTTP_HOST']); //Deletes the Auto Coookie
		session_unset();
		$this->report("User Logged out");
	}
	
	private function setCookie(){
		$value = $this->pass;
		$value .= ":";
		$value .= $this->id;
		setcookie("auto",$value,strtotime("+15 days"),"/",".".$_SERVER['HTTP_HOST']);
		$this->report("Cookies have been updated for auto login");
	}
	
	function update_session($d){
		$_SESSION = $d;
		$_SESSION['signed'] = true;
		
		$this->id = $d['user_id'];
		$this->username = $d['username'];
		$this->pass = $d['password'];
		$this->signed = true;
		
		$this->report("session updated");
	}
	
	function update_from_session(){
		$d = $_SESSION;
		
		$this->id = $d['user_id'];
		$this->data = $d;
		$this->username = $d['username'];
		$this->pass = $d['password'];
		$this->signed = true;
	}
	function hash_pass($pass){
		$salt = $this->salt;
		$this->pass = md5($salt.$pass.$salt);
		return $this->pass;
	}
	
	function report($str=false){
		$index = $this->log;
		if($str){
			$str = ucfirst($str);
			$this->console['reports'][$index][] = $str; //Strore Report
		}else{
			if($index){
				return $this->console['reports'][$index]; //Return the $index Reports Array
			}else{
				return $this->console['reports']; //Return the Full Reports Array
			}
		}
	}

	function error($str=false){
		$index = $this->log;
		if($str){
			$str = ucfirst($str); //Style String by making first character uppercase
			$this->console['errors'][$index][] = $str; //Strore Error
			$this->report("Error: {$str}"); //Report The error
		}else{
			if($index){
				return $this->console['errors'][$index]; //Return the $index Errors Array
			}else{
				return $this->console['errors']; //Return the Full Error Array
			}
		}
	}
	
	//Adds fields with errors to the console
	function form_error($field=false){
		$index = $this->log;
		if($field){
			$this->console['form'][$index][] = $field;	
		}else{
			if($index){
				return $this->console['form'][$index]; //Return the $index Errors Array
			}else{
				return $this->console['form']; //Return the Full form Array
			}
		}
	}
	
	//Check for errors in the console
	function has_error(){
		//Check for errors
		if($this->console['errors'][$this->log] != ""){
			$count = count($this->console['errors'][$this->log]);
			$this->report("$count Error(s) Found!");
			return true;
		}else{
			$this->report("No Error Found!");
			return false;
		}
	}
	
	//Generates a unique comfirm hash
	function uConfirm($length = false){
		$code = md5(uniqid(rand(), true));
		if($length != false){
			$this->confirm = substr($code, 0, $length);
		}else{
			$this->confirm = $code;
		}
	}
	
	//Test field in database for a value
	function check_field($field, $val, $str=false){
		$query = mysql_query("SELECT {$field} FROM users WHERE {$field}='{$val}' ");
			if(mysql_num_rows($query) >= 1){
				if($str){
					$this->error($str);
				}else{
					$this->error("There was a match for  $field = $val");
				}
				return true;
			}else{
				$this->report("No Match on Field $field=$val");
				return false;
			}
	}
	
	//Executes SQL query and checks for success
	function check_sql($sql,$debug = false){
		$this->report("SQL: {$sql}"); //Log the SQL Query first
		if (!mysql_query($sql)){
			if($debug){
				$this->error(mysql_error());
			}
			return false;
		}else{
			return true;
		}
	}
	
	//Executes SQL query and expects a change in database
	function check_sql_change($sql,$debug = false){
		$this->report("SQL: {$sql}"); //Log the SQL Query
		if (!mysql_query($sql)){
			if($debug){
				$this->error(mysql_error());
				return false; //die('Error: ' . mysql_error());
			}else{
				return false;
			}
		}else{
			$rows = mysql_affected_rows();
			if($rows > 0){
				//Good, Rows where affected
				$this->report("$rows row(s) where Affected");
				return true;
			}else{
				//Bad, No Rows where Affected
				$this->report("No row was Affected");
				return false;
			}
		}
	}
	
	//Validates field($name) in tmp_data
	private function validate($name,$limit,$regEx=false){
		$str = $this->tmp_data[$name];
		$l = explode("-",$limit);
		$min = intval($l[0]);
		$max = intval($l[1]);
		if(!$max || !$min){
			$this->report("Invalid second paramater for the private validate method on $name");
			return false;
		}
		if(!$str){
			if(!isset($this->tmp_data[$name])){
				$this->error("missing index $name from the POST array");
			}
			if(strlen($str) == $min){
				$this->report("$name is blank and optional - skipped");
				return true;
			}
			$this->error("$name is required");
			$this->form_error($name);
			return false;
		}
		if(strlen($str) > $max){
			$this->error("The $name is larger than $max character/digits");
			$this->form_error($name);
			return false;
		}
		if(strlen($str) < $min){
			$this->error("The $name is too short. it should at least be $min character/digits long");
			$this->form_error($name);
			return false;
		}
		if($regEx){
			preg_match_all($regEx,$str,$match);
			//print_r($match);echo count($match[0])."+";
			
			if(count($match[0]) != 1){
				$this->error("The $name \"{$str}\" is not valid");
				$this->form_error($name);
				return false;
			}
		}
		
		$this->report("The $name is Valid");
		return true;
	}
}
	


?>

Initial URL
http://crusthq.com/projects/uflex/

Initial Description
uFlex or userFlex is an all in one PHP user class that provides developers with a simple yet secured, reliable and flexible framework like structure to authenticate their sites and applications. The purpose of uFlex is to handle the tedious processes involved in authenticating users.

Initial Title
All in one PHP user class - uFlex

Initial Tags
class, user

Initial Language
PHP