All pastes #986594 Raw Edit

Anonymous

public php v1 · immutable
·published 2008-04-15 22:07 UTC
rendered paste body
<?/**************************************************  users.class.php -- *  - Active user interface, cannot be substituted as generic comparison objects. * Built in controls to maintain, restrict, check & even alter access, automatically *  syncronizing the database in the process* *  Copyright(c) 2008 and beyond, Vanguard Technical and Media Services*  - Author Bryan Steimel (04.02.08)*************************************************/// depends, db.class.php(db.abstract.php) - db.tables.phpdefine('USERS_TABLE_USER', 'ueas_user'); define('UACCOUNT_ATTEMPTS_LIMIT', 10);define('UACCOUNT_ATTEMPTS_LOCK', 60*60*6); // SECONDS! 6 hours.. failed attempts count reset after this same time period.. define('UACCOUNT_PWDMIN', 8);define('UACCOUNT_DEFAULT_FLAGS', 0);define('UACCOUNT_DEFAULT_FLAGS', 0);define('UACCOUNT_COOKIE_SSL', false); define('UACCOUNT_COOKIE_DOMAIN', 'test.europeanautosource.com'); define('UACCOUNT_COOKIE_PATH', '/admin/'); define('UACCOUNT_COOKIE_CAP', '__'); define('UACCOUNT_COOKIE_INACTIVE_EXPIRY', 0);define('UACCOUNT_AUTH_OFFSET', 2); define('UACCOUNT_AUTH_LEN', 30); // register users table; new dbt(USERS_TABLE_USER, array(     username => array(DBT::TEXT, 32),	hash  => array(DBT::TEXT, 32),	authcode =>  array(DBT::TEXT, 30), 	attempts_failure => array(DBT::INT, 2), 	attempts_last => array(DBT::INT, 11), 		lock_expiry	=> array(DBT::INT, 11), 	account_status =>  array(DBT::INT, 3, 1), 	logon_last =>  array(DBT::INT, 11), 	id => DBT::DENY  ));     // fuck it, I'm gonna throw exceptions as opposed to implementing an event handler..   class UExcept extends Exception {  const INTERNAL = 2;  const GENERIC = 0;   const WARNING = 1;   const LOCK = 100;     public $messages = array();  public $codes = array();   public $code = self::INTERNAL;   public function __construct($message = '', $code = self::INTERNAL) {  	  $this->message = $message; $this->message = $this->message(); 	  parent::__construct($this->format(), $code); //	parent::__construct($message, $code);   }   public function message() {     if(is_numeric($this->message) && array_key_exists($this->message, $GLOBALS['UE_MESSAGES']))       return $GLOBALS['UE_MESSAGES'][$this->message]; 	elseif(is_array($this->message)) { 	  $this->codes = array_values($this->message); 	  foreach($this->message as $val) 	    array_push($this->messages, ($GLOBALS['UE_MESSAGES'][$val] 	      ? $GLOBALS['UE_MESSAGES'][$val] 		    : 'An unknown error has occured')		);      return (array)$this->messages;     }	else	  return parent::getmessage();   }   public function format($format = ' &nbsp; - %s<br />') {     $txt = ''; 	if(is_array($t = $this->messages ? $this->messages : $this->getmessage())) 	  foreach($t as $v) $txt .= sprintf($format, $v) . "\n";    else	  $txt = $t; 	return $txt;   }   public function register(array $list) { foreach($list as $k => $v) $GLOBALS['UE_MESSAGES'][$k] =  $list[$k]; }   public function uexit($message=null) {    if($message) 	  print '<b>' . $message . '<\b>' . "\n"; 	exit(2);  } } $dbd = $GLOBALS['DBD']; //non-method functions, getting too cluttered down there is all.. class user_tools {   const U_OK = true,     ERR_EXISTS = 101, 	ERR_INVALID_USER = 111,	ERR_INVALID_PWD = 121, 	ERR_INVALID_EMAIL = 131;    static function retrieve($user, $id = false) { // $user/$id not interchangable! this will insure, its deliberate    global $dbd;     if(empty($user) && (!$id && is_null($user))) 	    throw(new UExcept(__CLASS__ . '::retrieve() No user supplied'));     $res = $dbd->query(	  $b = "SELECT username, hash, authcode, authsess, email, request_last, account_status, logon_last, lock_expiry, attempts_last, attempts_failure, id " . 	   "FROM " . USERS_TABLE_USER . " WHERE " . 	    ($id		  ? "id = '" . dbd::sanitize($user) . "'"		    : "lcase(" . (!strpos($user, '@') ? 'username' : 'email') . ") = '" . dbd::sanitize($user)  . "'"	    )	  ); 	return is_object($res) && $res->rows()	  ? $res : false;   }    static public function add($username, $email, $password, $verify = true) {     global $dbd;     $e = array();     if(($res = self::retrieve($username)) !== false) 	  throw(new UExcept(self::ERR_EXISTS, UExcept::WARNING)); 	if(!preg_match('/^[^0-9][a-z0-9\.\-\_]+$/i', $username))	  $e[] = self::ERR_INVALID_USER; /* do this second, as to go ahead and account for any possibility in the db */	if(strlen($password) < UACCOUNT_PWDMIN) 	  $e[] = self::ERR_INVALID_PWD; 	if(!preg_match('/^(?:(?!@).*)@.*\.[a-z]{2,}$/', $email))	  $e[] = self::ERR_INVALID_EMAIL;     if(!empty($e)) 	  throw(new UExcept($e, UExcept::WARNING)); 	$dbd->insert(USERS_TABLE_USER, array(         'account_status' => UACCOUNT_DEFAULT_FLAGS, 		'username' => $username, 		'email' => $email, 		'authcode' => self::gen_auth(),		'hash' => self::crypt($password, $username)      )      ); 		return $dbd->affected() 	  ? true : false;   }   static protected function gen_auth($chars = 30) {     $rchrs = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQURSTUVWXYZ0123456789';     for($rstr = '', $i = 1, $len = strlen($rchrs) - 1; $i <= $chars; $i++) 	  $rstr .= substr($rchrs, mt_rand(0, $len), 1); 	return $rstr;  }   static public function crypt($text, $salt) { return md5(md5($text . strtolower($salt))); }       const ID = 1, SESS = 0;   static function cookiejar() {     foreach($_COOKIE as $k => $v) { 	  if(preg_match('/^__[a-z0-9]+$/i', $k) && is_numeric($v) && (strlen($k) == (UACCOUNT_AUTH_LEN + 2))) {        return array($k, $v);	  		break;	      }    }	return false;  } }  UExcept::register(array(   user::ERR_EXISTS => 'The username you requested is already in use. Please, choose another.',    user::ERR_INVALID_USER => 'The username you requested contains invalid characters. Please choose another. <i>(Acceptable characters include A-Z, 0-9, _, - and .)</i>',   user::ERR_INVALID_PWD => 'Password must be atleast ' . UACCOUNT_PWDMIN . ' characters long.',   user::ERR_INVALID_EMAIL => 'The email address you supplied is invalid.')); class user extends user_tools {    public $data = array();   public $update = array();   public $error;   protected $synced = false;   protected $queried = false;       public $cookie;   public $id; // just to keep it outta the update array!   static $dbd;     const UFLG_ACCT_LOCK = 0x001,     UFLG_ACCT_ROOT = 0x002;  /* 0x004, 0x006, 0x008, 0x0010 */     public function __construct($user = null, $id = false) {  // less flexibility more security...     if(is_null($user) && !$id) { 	  if($this->cookie())	     return $this->__construct($this->cookie[parent::SESSION], true); 	} else {      $this->data['username'] = $user; 	  $this->update = array(); 	}	if(!$this->sync($user, $id))	  throw(new UExcept('', self::ERR_NOSUCHUSER));  }    public function sync($user = null, $id = false) {	  	if(($res = parent::retrieve(($user = is_null($user) ? $this->username : $user), $id))) {       $this->update = array();       if(($this->data = $res->get())) { 	  	 $this->id = $this->data['id'];         return ($this->synced = $this->queried = true);       }	}     throw(new UExcept('Unable to sync \'' . $this->username . '\''));  // security...   }   public function synced() { return $this->queried && $this->synced; } // this only exists incase I need to add/change conditions later..   public function locked() {    	if($this->status(self::UFLG_ACCT_LOCK)) {  	  if(!$this->lock_expiry || $this->lock_expiry > time()) 	    return true;	  $this->ulock();	      		   }	return false;  }   public function authenicate($pass, $auth = null) { // $pass is plaintext!     if(empty($pass) || !isset($this->username)) 	  throw(new UExcept('authenicate(); \'username\' or \'password\' undefined'));    if(!$this->queried && !$this->sync())       return false; // this will never happen	echo " lock: " . ($this->locked() ? 'yes' : 'no') . "<br />"; 	if($this->locked())	  throw(new UExcept('This account has been locked, please check your email or consult site administration.', UExcept::LOCK));	if(($result = (	    strlen($this->username) 		  && !empty($this->hash) 		  && $this->synced()		  && ($this->hash === parent::crypt($pass, $this->username))      ) 	    ? true : false) === false	) { 	  $this->attempts_failure = $this->attempts_failure + 1; echo "unf: " . $this->attempts_last . " expiry: " . ($this->attempts_last + UACCOUNT_ATTEMPTS_LOCK) . " > " . time() . "<br />";    //   if(!$this->attempts_last) // do we have a record? if, no its the first.. if left to 0 it will always expire..       //	$this->attempts_last = time();       if($this->attempts_last && ($this->attempts_last+UACCOUNT_ATTEMPTS_LOCK) > time()) { // has attempts time expired?	    if($this->attempts_failure >= UACCOUNT_ATTEMPTS_LIMIT) { 	      $this->lock(UACCOUNT_ATTEMPTS_LOCK); 	      return $this->authenicate($pass, $auth); // throw an exception at em	    }	  }       elseif(!$this->attempts_last) {  // it has expired, reset it	    $this->attempts_last = time();		$this->attempts_failure = 1;	  } 	   	}     return $result;   }       public function active() { // do we have a good cookie!     if(!$this->cookie())	  return false;	  }   // http://i140.photobucket.com/albums/r8/oatmeal_el/abortionomelets.jpg  public function cookie($renew = false) {     if($renew) { // find it. 	  if(!$this->queried) 	    return false; 	  $this->logon_last = time(); 	  if(!setcookie(	    UACCOUNT_COOKIE_CAP . ($this->authsess = parent::gen_auth()), 		$this->id, 		0, 		UACCOUNT_COOKIE_PATH, 		UACCOUNT_COOKIE_DOMAIN, 		UACCOUNT_COOKIE_SSL, 		true) 	   ) throw(new UExcept(__CLASS__ . '::cookie: Header output already began...')); 	  $this->commit();     } else {		  if($cookie = $this->cookie ? $this->cookie : parent::cookiejar()) { 	    if(($this->request_last < (time() - UACCOUNT_COOKIE_INACTIVE_EXPIRY))		  && (substr($cookie[parent::SESSION], 2) === $this->authsess)		  ) $this->cookie = $cookie; 	    else          $this->remove_cookie($cookie[parent::SESSION]);       //    setcookie($cookie[parent::SESSION], '', time() - (60*60*24));	  	  }	    echo substr($cookie[parent::SESSION], 2) . ' - ' . $this->authsess . '<br />'; 	  $this->request_last = time();     }    return $this->cookie;	  }     public function remove_cookie($key = null) {     if(!($key = ($key ? $key : $this->cookie[parent::SESSION])))	  return false;   	return @setcookie(	       $key, 		   $this->id, 		   (time() - 60*60*24*30), 		   UACCOUNT_COOKIE_PATH, 		   UACCOUNT_COOKIE_DOMAIN, 		   UACCOUNT_COOKIE_SSL, 		   true	);  }  public function lock($expiry) { // do this as a method, then i can be updated with a commit() call, saving any additional queries aside from it  echo "LOCKING!"; 	$this->lock_expiry = time() + $expiry; 	$this->authcode = parent::gen_auth();    $this->status(self::UFLG_ACCT_LOCK, '+');	$this->commit();  }    public function ulock() {    if(!$this->status(self::UFLG_ACCT_LOCK)) 	  return false; 	  	echo "UNLOCKING!"; 	$this->lock_expiry = 0; 	$this->attempts_last = 0; 	$this->attempts_failure = 0;   	$this->status(self::UFLG_ACCT_LOCK, '-');	$this->commit();  }   protected function status($flag = null, $op = null) { // $this->flags(FLAG, +|-)    $stat = $this->account_status;    if(is_null($flag)) 	  return $stat; 	elseif($op == '-' && $this->status($flag)) 	    $this->account_status = $stat - $flag; 	elseif($op == '+' && !$this->status($flag))	  $this->account_status = $stat + $flag;     return ($flag & $this->account_status);   }     // signal/modify/report more *normal* actions taken, those not affecting flow control  public function __set($n, $v) {   // if(isset($this->data[$n]) && $this->data[$n] === $v) 	 // return;//	 echo " -- Setting data " . $n . "  to '" . $v . "'<br />\n";     $this->synced = false;     $this->update[$n] = 1;     $this->data[$n] = $v;   }    public function __get($n) {  	if(isset($this->data[$n])) { 	//  echo "Serving data: " . $n . " = " . $this->data[$n] . "<br />\n";	  return $this->data[$n];	  } 	throw(new UExcept('Requested user data, element \'' . $n .'\' undefined.')); // this will be annoying, but will force care to be taken with data - which is always a fine, fine idea  }    public function __isset($n) { return isset($this->data[$n]); }   public function __unset($n) { $this->$n = null; } // dont do anything in this instance..    public function commit() {     echo "COMMITING!";     global $dbd;     if(empty($this->update) || !is_array($this->update)) 	  return false; 	$bastard = array(); 	try {      foreach($this->update as $k => $v) $bastard[$k] = $this->$k; 	} catch(UExcept $ux) { 	  throw(new UExcept('It seems someone went and attached irrelevent data to the user object, stop that.. (' . $ux->getmessage() . ')' )); 	}	$b =  ;	$this->update = array(); 	if(!empty($bastard)) 	  return $dbd->exec(	    "UPDATE " . USERS_TABLE_USER . " " . 	      "SET " . dbd::create_dstruct(dbd::UPDATE, $bastard) .  " " . 	     "WHERE id = '" . dbd::sanitize($this->id) . "'"	  );   }      public function __destruct() { }   public function logout() {     $this->remove_cookie();	$this->authsess = ''; 	$this->__destruct();   }   } class admin extends user { } ?>