Location: PHPKode > projects > Simple Way to Usenet > libs/core/auth/realm.class.php
<?php
/**
 * 
 * 
 * @author  Benjamin Gillissen <hide@address.com>
 * 
 *	**************************************************************

	Copyright (C) 2009  Benjamin Gillissen
	
	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 2
	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 at:
	http://www.gnu.org/copyleft/gpl.html

 *	**************************************************************
 */
class realm extends realm_ticket {
	
	private static $neededconf = Array('cookname', 'chals', 'accs');
	
	private static $REALM;
	private static $AUTH;

	private $accounts, $challenges, $acl;

	public function __construct($realm){
		if ( FALSE === @constant('CORE_AUTH') ){ return; }
		parent::__construct($realm);
		if ( isset(self::$REALM[$realm]) ){ return; }
		if ( FALSE === configs::get('realm', 'realm') ){ errors::raise("Realm $realm : is unknown !", CORE_LOG_ALERT, 'REALM'); }
		//checking some realm configs,
		foreach(self::$neededconf as &$var){
			if ( FALSE === configs::get('realm', 'realm', Array($realm, $var)) ){
				errors::raise("Realm $realm : missing '$var' option", CORE_LOG_ALERT, 'REALM');
			}
		}
		//cookies handler init, the first realm loaded set it, we do not overwrite.
		//a better solution would be to loop realms and take the biggest lifetime,
		//hmm sess use it too
		$COOKRef = configs::get('realm', 'realm', Array($realm, 'cookname'));
		if ( FALSE === configs::get('cookies', $COOKRef) ){
			errors::raise("Realm $realm : is using cookies channel $COOKRef", CORE_LOG_DEBUG, 'REALM');
			$lifetime = configs::get('realm', 'realm', Array($realm, 'cooklifetime'));
			if ( FALSE === $lifetime ){
				errors::raise("Realm $realm : missing 'cooklifetime' option, using 30days as default", CORE_LOG_WARNING, 'REALM');
				configs::set('realm', 'realm', 60*60*24*30, Array($realm, 'cooklifetime'));
				$lifetime = 60*60*24*30;
			}
			$conf = Array('cipher'=>FALSE, 'expire'=>$lifetime);
			configs::set('cookies', $COOKRef, $conf);
		}
		$this->cleanup_tickets();
		$this->cleanup_client_tickets();
		self::$REALM[$realm] = Array();
	}
	
	
//-------------------------------------------------------------------Object load

	private function load_accounts(){
		if ( isset($this->accounts) ){ return; }
		if ( ! isset(self::$REALM[$this->realm]['accs']) ){
			$accs 	= configs::get('realm', 'realm', Array($this->realm, 'accs'));
			errors::raise("Loading ".count($accs)." Account engine for realm '$this->realm'", CORE_LOG_DEBUG, 'REALM');
			self::$REALM[$this->realm]['accs'] = new realm_accounts($this->realm, $accs);
			unset($accs);
		}
		$this->accounts = &self::$REALM[$this->realm]['accs'];
		return;
	}
	
	private function load_challenge($chal){
		if ( ! isset(self::$REALM[$this->realm]['chals'][$chal]) ){
			$chals 	= configs::get('realm', 'realm', Array($this->realm, 'chals'));
			if ( FALSE === array_search($chal, $chals) ){ errors::raise("Challenge '$chal' is invalid for realm '$this->realm' !", CORE_LOG_ALERT, 'REALM'); }
			unset($chals);
			$type = configs::get('realm', 'chal', Array($chal, 'type'));
			if ( FALSE === $type ){ errors::raise("Challenge '$chal' used in realm '$this->realm' is missing 'type' config !", CORE_LOG_ALERT, 'REALM'); }
			$class = 'chal_'.$type;
			self::$REALM[$this->realm]['chals'][$chal] = new $class($this->realm, $chal);
		}
		$this->challenges = &self::$REALM[$this->realm]['chals'][$chal];
		return;
	}
	
	private function load_acl(){
		if ( ! isset(self::$REALM[$this->realm]['acl']) ){
			$engine = configs::get('realm', 'realm', Array($this->realm, 'acl_engine'));
			$c = "acl_$engine";	
			self::$REALM[$this->realm]['acl'] = new $c($this->realm);
		}
		$this->acl = &self::$REALM[$this->realm]['acl'];
		return;
	}

//-------------------------------------------------------------------Object load

	
	
//-------------------------------------------------------------------REMEMBER AUTHENTIFICATION

	//tell if client got a active ticket on this realm
	public function isloged(){
		if ( FALSE === @constant('CORE_AUTH') ){ return FALSE; }
		if ( isset(self::$AUTH[$this->realm]['ruid']) ){ return TRUE; }
	}
	
	//return the active ruid for this client on this realm
	public function logedas(){
		if ( !$this->isloged() ){ return FALSE; }
		return self::$AUTH[$this->realm]['ruid'];
	}
	
	public function logedpassive(){
		if ( FALSE === @constant('CORE_AUTH') ){ return FALSE; }
		$tickets = $this->get_passive_ticket();
		if ( FALSE === $tickets ){ return FALSE; }
		foreach($tickets as $k => &$ticket){ $o[] = $this->ticket_uid($ticket);	}
		if ( isset($o) ){
			errors::raise('Loged passive as :'.print_r($o, TRUE), CORE_LOG_NOTICE, 'realm'); 
			return $o; 
		}
		return FALSE;
	}
	
	//return the membership of the gived ruid (of client if non gived)
	public function ismemberof($ruid=NULL){
		if ( FALSE === @constant('CORE_AUTH') ){ return Array(0=>Array(),1=>Array()); }
		if ( NULL !== $ruid ){
			$this->load_accounts();
			return self::$REALM[$this->realm]['accs']->ismemberof($ruid);			 
		} elseif ( TRUE === $this->isloged() ){ 
			return self::$AUTH[$this->realm]['mbr'];
		}
		return Array(0=>Array(),1=>Array());
	}
	
	public function switchto($ruid){
		if ( FALSE === @constant('CORE_AUTH') ){ return FALSE; }
		$err = $this->activate_ticket($ruid);
		if ( TRUE !== $err ){ 
			errors::raise("Switching To account ".$this->loginbyuid($ruid)." failed : $err", CORE_LOG_ERROR, 'REALM');
			return FALSE;
		}
		errors::raise("Switching To account $ruid on realm $this->realm", CORE_LOG_INFO, 'REALM');
		pagegen::add_event("Account ".$this->loginbyuid($ruid)." is now active on realm $this->realm");
		self::$AUTH[$this->realm]['ruid'] = $ruid;
		self::$AUTH[$this->realm]['mbr'] = $this->ismemberof($ruid);
		sessions::start('profile');
		sessions::clear('profile');
		return TRUE;
	}
	
	public function logout(){
		if ( FALSE === @constant('CORE_AUTH') ){ return; }
		unset(self::$AUTH[$this->realm]['ruid']);
		unset(self::$AUTH[$this->realm]['mbr']);
		sessions::start('profile');
		sessions::clear('profile');
		pagegen::add_event("Active account has been loged out from realm $this->realm");
		$this->del_active_ticket();
		$ptickets = $this->get_passive_ticket();
		if ( !is_array($ptickets) ){ return; }
		$k = array_keys($ptickets);
		$ruid = $this->ticket_uid($ptickets[$k[0]]);
		$this->switchto($ruid);
	}
	
//-------------------------------------------------------------------REMEMBER AUTHENTIFICATION
	
	

//-------------------------------------------------------------------CHALLENGE

	public function authenticate(){
		if ( FALSE === @constant('CORE_AUTH') ){ return FALSE; }
		if ( $this->isloged() ){ return TRUE; } 
		$ticket = $this->get_active_ticket();
		$ptickets = $this->get_passive_ticket();
		if ( FALSE !== $ticket ){
			//first we try to restore the client_active_ticket,	 
			$ruid = $this->ticket_uid($ticket);
			errors::raise("Client @ ".client::host()." has been authenticated as account $ruid on realm $this->realm", CORE_LOG_INFO, 'REALM');
			self::$AUTH[$this->realm]['ruid'] = $ruid;
			self::$AUTH[$this->realm]['mbr'] = $this->ismemberof($ruid);
			$this->ticket_update($ticket);
			$ptickets = $this->get_passive_ticket();
			if ( !is_array($ptickets) ){ return; }
			foreach($ptickets as $k => $pticket){ $this->ticket_update($pticket); }
			
		} elseif ( is_array($ptickets) ){
			//then we try to restore the first client_passive_ticket,
			$k = array_keys($ptickets);
			$ruid = $this->ticket_uid($ptickets[$k[0]]);
			$this->switchto($ruid);
			foreach($ptickets as $k => $pticket){ $this->ticket_update($pticket); }
			
		}
		//no active, passive ticket, let's attempt an auto_login
		return $this->auto_login();	
	}
	
	public function login($chal=NULL, $err=NULL, $forceform=FALSE){
		if ( FALSE === @constant('CORE_AUTH') ){ return; }
		if ( $chal === NULL ){
			errors::raise("Using first challenge defined in realm $this->realm", CORE_LOG_NOTICE, 'REALM');
			$chal = configs::get('realm', 'realm', Array($this->realm, 'chals', 0)); 
		}
		$this->load_challenge($chal);
		$trying = $this->challenges->istrying();
		if ( FALSE === $trying OR $err !== NULL ){
			$chaldata = $this->challenges->get_chaldata($chal);
			$ticket = CORE::hash();
			$msg = $this->newticket($ticket, $chaldata);
			if ( TRUE !== $msg){
				$b = $msg;
				if ( NULL !== $err ){ $b = Array($msg, $err); }
				$this->challenges->sendform($b);return; 
			}
			$this->challenges->seticket($ticket);
			$this->challenges->sendform($err);
		}
		//errors::raise("Checking posted challenge", CORE_LOG_NOTICE, 'REALM');
		//so client has sent back his challenge, let's see that
		$ticket = $this->challenges->geticket();
		//ticket could not be restored from challenge
		if ( FALSE === $ticket ){ $this->login($chal, 'realmsg_nochalticket');return; }
		//challenge returned a ticket, is it valid?
		if ( FALSE === $this->isticket($ticket) ){ $this->login($chal, 'realmsg_noticket');return; }
		//some challenges require some more data, let's get back those to do the preCheck
		$chaldata = $this->ticket_chaldata($ticket);
		if ( FALSE === $chaldata ){ $this->login($chal, 'realmsg_nochaldata');return; }
		//some specific challenge pre check,
		$err = $this->challenges->preCheck($chaldata);
		//errors::raise("PreCheck posted challenge => ".$err, CORE_LOG_NOTICE, 'REALM');
		if ( TRUE !== $err ){ $this->login($chal, $err);return; }
		if ( TRUE === $this->ischallenged($ticket) ){ $this->login($chal, 'realmsg_alreadychallenged');return; }
		$this->mark_challenged($ticket);
		$this->load_accounts();
		$ruid = $this->challenges->authenticate();
		//auth check failed, we sendform
		if ( FALSE === $ruid ){ $this->login($chal, 'realmsg_authko');return; }
		//auth ok we grant ticket
		/*
		$err = $this->apply_login($ruid, $ticket);
		if ( TRUE !== $err ){ $this->login($chal, $err);return; }
		*/
		$err = $this->grant_ticket($ruid, $ticket);
		if ( TRUE !==  $err){ $this->login($chal, $err);return; }
		//errors::raise('Granting "'.$ruid.'" to '.client::host(), CORE_LOG_INFO, 'REALM');
		self::$AUTH[$this->realm]['ruid'] = $ruid;
		self::$AUTH[$this->realm]['mbr'] = $this->ismemberof($ruid);
		
		errors::raise("Challenge success, ".client::host()." is now authenticated as $ruid", CORE_LOG_INFO, 'REALM');
		pagegen::add_event("Challenge '$chal' success, you are now authenticated as ".$this->loginbyuid($ruid) );
		sessions::clear('profile');
		$this->accounts->update_lastlogin($ruid);
		if ( FALSE ){	//so user asked for autologin FIXME, new challenge method
			errors::raise("Storing autologin cooky key", CORE_LOG_NOTICE, 'REALM');
			$key = CORE::hash();	
			if ( FALSE === $this->set_client_autokey($ruid, $key) AND $forceform ){ $this->login($chal, 'realmsg_autocooknoset');return; }
			if ( FALSE === $this->accounts->set_cookey($ruid, $key) AND $forceform ){ $this->login($chal, 'realmsg_autoaccnoset');return; }
		} else {
			errors::raise("Removing autologin cooky key", CORE_LOG_NOTICE, 'REALM');
			$this->del_client_autokey();
		}
		if ( $forceform === TRUE ){ $this->login($chal, 'realmsg_authok');return; }
		return;
	}
	
//-------------------------------------------------------------------CHALLENGE


	
//-------------------------------------------------------------------AUTO LOGIN
		
	public function clientuseautologin(){
		if ( FALSE === @constant('CORE_AUTH') ){ return FALSE; }
		if ( FALSE === configs::get('realm', 'realm', Array($this->realm, 'auto_login')) ){ return FALSE; }
		$COOKRef = configs::get('realm', 'realm', Array($this->realm, 'cookname'));
		if ( !cookies::isdefined('autolog', $COOKRef) ){ return FALSE; }
		$autolog = cookies::read('autolog', $COOKRef);
		return isset($autolog[$this->realm]);
	}
	
	private function auto_login(){
		if ( FALSE === @constant('CORE_AUTH') ){ return FALSE; }
		if ( FALSE === $this->clientuseautologin() ){ return FALSE; }
		$COOKRef = configs::get('realm', 'realm', Array($this->realm, 'cookname'));
		$autolog = cookies::read('autolog', $COOKRef);
		$ruid = $autolog[$this->realm][0];
		$key = $autolog[$this->realm][1];
		unset($autolog, $COOKRef);
		$this->load_accounts();
		if ( FALSE === $this->accounts->cookcheck($ruid, $key) ){ return FALSE;	}
		$ticket = $this->newticket();
		if ( FALSE === $ticket ){ return FALSE; }
		if ( TRUE !== $this->apply_login($ruid, $ticket) ){ return FALSE; }
		$this->accounts->update_lastlogin($ruid);
		$key = CORE::hash();
		$this->set_client_autokey($ruid, $key);
		$this->account->set_cookey($ruid, $key);
		return TRUE;
	}

	private function set_client_autokey($who, $key){
		if ( FALSE === configs::get('realm', 'realm', Array($this->realm, 'auto_login')) ){ return FALSE; }
		$COOKRef = configs::get('realm', 'realm', Array($this->realm, 'cookname'));
		$autolog = cookies::read('autolog', $COOKRef);
		$autolog[$this->realm] = Array($who, $key);
		return cookies::set('autolog', $autolog, $COOKRef);
	}
	
	private function del_client_autokey(){
		$COOKRef = configs::get('realm', 'realm', Array($this->realm, 'cookname'));
		$autolog = cookies::read('autolog', $COOKRef);
		unset($autolog[$this->realm]);
		return cookies::set('autolog', $autolog, $COOKRef);
	}
	
//-------------------------------------------------------------------AUTO LOGIN	
	


//-------------------------------------------------------------------ACL ACCESS RIGTHS	
	
	public function ispublic($obj, $action, $objid){
		if ( FALSE === @constant('CORE_AUTH') ){ return TRUE; }
		$this->load_acl();
		//is everybody allowed to do $action on object $obj with id $objid ?
		$r = $this->acl->ispublic($obj, $action, $objid);
		//errors::raise("realm::ispublic($obj, $action, $objid) => $r", CORE_LOG_NOTICE, 'REALM');
		return $r;
	}
	
	public function checks($obj, $action, $objid){
		if ( FALSE === @constant('CORE_AUTH') ){ return TRUE; }
		$this->load_acl();
		$ruid = $this->logedas();
		$mbrship = $this->ismemberof();
		//echo "auth check on $obj<=>$action as $uid <=> ".print_r($mbrship, TRUE)."<br/>\n";
		$r = $this->acl->checks($obj, $action, $ruid, $mbrship, $objid);
		//errors::raise("realm::checks($obj, $action, $objid) => $r", CORE_LOG_NOTICE, 'REALM');
		return $r;
	}
	
	public function chmod_checks($obj, $ac=NULL){
		if ( FALSE === @constant('CORE_AUTH') ){ return TRUE; }
		$this->load_acl();
		$uid = $this->logedas();
		$mbrship = $this->ismemberof();
		$action = 'chmod';
		if ( $ac !== NULL ){ $action = "chmod_$ac"; }
		$r = $this->_RIGHT->checks('objects', $action, $uid, $mbrship, $obj);
		//echo "cchmod check => obj:$obj, action: $action => $r<br>";
		//echo "<pre>";print_r($mbrship);echo "</pre>";
		return $r;
	}
	
//-------------------------------------------------------------------ACL ACCESS RIGTHS
	
	
	
//-------------------------------------------------------------------ACCOUNTS INFOS
		
	public function loginbyuid($ruid){
		if ( FALSE === @constant('CORE_AUTH') ){ return FALSE; }
		$this->load_accounts();
		return $this->accounts->infobyuid('login', $ruid); 
	}
	
	protected function getpass($ruid){
		if ( FALSE === @constant('CORE_AUTH') ){ return FALSE; }
		$this->load_accounts();
		return $this->accounts->getpass($ruid); 
	}
	
	protected function authcheck($login, $pass){
		if ( FALSE === @constant('CORE_AUTH') ){ return FALSE; }
		$this->load_accounts();
		return $this->accounts->authcheck($login, $pass); 
	}

	protected function uidbylogin($login){
		if ( FALSE === @constant('CORE_AUTH') ){ return FALSE; }
		$this->load_accounts();return $this->accounts->uidbylogin($login); 
	}
	
	public function groupbygid($rgid){
		if ( FALSE === @constant('CORE_AUTH') ){ return FALSE; }
		$this->load_accounts();return $this->accounts->groupbygid($rgid); 
	}
		
//-------------------------------------------------------------------ACCOUNTS INFOS

	
	
	
	public function get_profiledata(){
		if ( FALSE === @constant('CORE_AUTH') ){ return FALSE; }
		$o['user'] = $this->loginbyuid($this->logedas());
		$ticket = $this->get_active_ticket();
		$tmp['granted'] = time() - $this->grant_time($ticket);
		$tmp['exp'] = configs::get('realm', 'realm', Array($this->realm, 'lifetime')) - (time() - $this->create_time($ticket) );
		$tmp['to'] = configs::get('realm', 'realm', Array($this->realm, 'timeout'));
		foreach($tmp as $k => &$int){
			if ( $int <= 60 ){
				$o[$k] = $int.' secondes';
			} elseif( $int <= 3600 ){
				$o[$k] = floor($int/60).' '.langs::translate('minutes', NULL, 'base');
			} elseif( $int <= 86400 ){
				$o[$k] = floor($int/(60*60)).' '.langs::translate('hours', NULL, 'base');
			} else {
				$o[$k] = floor($int/(24*60*60)).' '.langs::translate('days', NULL, 'base');
			}
		}
		unset($tmp);
		$buff = $this->ismemberof();
		foreach($buff as $lvl => &$mbrship ){
			foreach($mbrship as $k => $grp ){
				$gname = $this->groupbygid($grp);
				if ( $lvl == 1 ){ $gname = '<b>'.$gname.'</b>'; }
				$o['mbrship'][] = $gname;
			}
		}
		unset($buff);
		$o['passchange'] = FALSE;
		$o['mailchange'] = FALSE;
		return $o;
	}
	
	public function list_users(){ return FALSE; }
	

}
Return current item: Simple Way to Usenet