<?php
/**
* a ticket is defined by
* a key => hashed, ticketkey (the thing client must provide in each request)
* a realm => hashed, realm ref
* a host => hashed, ip used to pass the challenge
* a ruid => crypted (notyet), the granted ruid
* a status => 1 (actif account), 0 (not granted), NULL (multilogin-passive)
* a creat date => when challenge was requested ? for lifetime, chal_timeout
* a chal date => when challenge was passed ?
* a lastgrant => when this ticket was last used by client ? (passive are also updated)
*
* got 4 status
* 1 - unchallenged => status=0, chaldate=0
* 2 - challenged failed => status=0, chaldate!=0
* 3 - granted active => status=1, chaldate!=0
* 4 - granted passive => status=NULL, chaldate!=0
*
* @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_ticket extends dbobject {
protected $realm;
private static $active_ticket;
public function __construct($realm){
$this->realm = $realm;
unset($realm);
$cnf = configs::get('realm', 'realm', Array($this->realm, 'ticket_opt'));
if ( FALSE === $cnf ){
errors::raise('Missing Realm Ticket DB CNF option for realm '.$this->realm, CORE_LOG_FATAL, 'REALM');
return;
}
parent::__construct($cnf);
}
//-------------------------------------------------------------------------------------CLEANUP
protected function cleanup_tickets(){
$aticket = $this->get_active_ticket();
$ptickets = $this->get_passive_ticket();
$to = configs::get('realm', 'realm', Array($this->realm, 'timeout'));
$timeout =( time() - $to );
$lifetime=( time() - configs::get('realm', 'realm', Array($this->realm, 'lifetime')) );
$chalto=( time() - configs::get('realm', 'realm', Array($this->realm, 'chal_timeout')) );
$arg=Array( 'realm'=>md5($this->realm), 'host'=>md5(client::host()), 'timeout'=>$timeout, 'lifetime'=>$lifetime, 'chalto'=>$chalto);
//Host Granted timeout (active / passive)
while( FALSE !== ( $ticket = $this->dbquery($arg, 'get_grantimeout', 'key')) ){
if ( md5($aticket) == $ticket ){
pagegen::add_event("Active Ticket has been droped : no activity within $to secondes");
//if is ajax, enforce dummy call (next req will take care of inactive TO, EXP or restore)
if ( isset($_GET['ajax']) ){ ajax::EnforceReq('dummy'); }
} elseif ( is_array($ptickets) ){
foreach($ptickets as $k => $pticket ){
if ( md5($pticket) == $ticket ){ pagegen::add_event("Passive Ticket has been droped : no activity within $to secondes"); }
}
}
$this->drop_ticket($ticket);
}
//Host Granted expire (active / passive)
while( FALSE !== ( $ticket = $this->dbquery($arg, 'get_grantexpire', 'key')) ){
if ( md5($aticket) == $ticket ){
pagegen::add_event("Active Ticket has been droped : maximum lifetime reached");
} elseif ( count($ptickets) != 0 ){
foreach($ptickets as $k => $pticket ){
if ( md5($pticket) == $ticket ){ pagegen::add_event("Passive Ticket has been droped : maximum lifetime reached"); }
}
}
$this->drop_ticket($ticket);
}
//Failed Challenge expire
while( FALSE !== ( $ticket = $this->dbquery($arg, 'get_failedexpire', 'key')) ){ $this->drop_ticket($ticket); }
//Unchallenged timeout
while( FALSE !== ( $ticket = $this->dbquery($arg, 'get_unchaltimeout', 'key')) ){ $this->drop_ticket($ticket); }
}
protected function cleanup_client_tickets(){
$utickets = $this->get_client_tickets();
if ( FALSE === $utickets ){ return FALSE; }
foreach($utickets as $k => $ticket){
if ( FALSE === $this->isticket($ticket) ){
errors::raise("Client Ticket [$k] as been cleaned", CORE_LOG_NOTICE, 'REALM');
unset($utickets[$k]);
}
}
return $this->set_client_tickets($utickets);
}
//-------------------------------------------------------------------------------------CLEANUP
//-------------------------------------------------------------------------------------CLIENT TICKET
private function get_client_tickets(){
//has set into client cookies
$COOKRef = configs::get('realm', 'realm', Array($this->realm, 'cookname'));
$utickets = cookies::read('tickets', $COOKRef);
unset($COOKRef);
if ( FALSE === is_array($utickets) ){ return FALSE; }
if ( FALSE === isset($utickets[$this->realm]) ){ return FALSE; }
//decode, decrypt, unserialize, $utickets[$this->realm]
//errors::raise('Ticket cookies has been restored with :'.print_r($utickets[$this->realm], TRUE), CORE_LOG_NOTICE, 'REALM');
return $utickets[$this->realm];
}
private function set_client_tickets($rtickets){
$COOKRef = configs::get('realm', 'realm', Array($this->realm, 'cookname'));
$utickets = cookies::read('tickets', $COOKRef);
//serialize, crypt, encode $rtickets
$utickets[$this->realm] = $rtickets;
//errors::raise('Ticket cookies has been updated with :'.print_r($rtickets, TRUE), CORE_LOG_NOTICE, 'REALM');
return cookies::set('tickets', $utickets, $COOKRef);
}
//return the active ticket from the client's tickets list
protected function get_active_ticket(){
$utickets = $this->get_client_tickets();
if ( FALSE === $utickets ){
errors::raise('No Ticket in cook', CORE_LOG_NOTICE, 'REALM');
return FALSE;
}
foreach($utickets as $k => $ticket){
//errors::raise('checking Ticket '.md5($ticket), CORE_LOG_DEBUG, 'REALM');
if ( TRUE === $this->isactif($ticket) ){ return $ticket; }
}
errors::raise('No active Ticket in cook', CORE_LOG_NOTICE, 'REALM');
return FALSE;
}
//return the passive tickets from the client's tickets list
protected function get_passive_ticket(){
$utickets = $this->get_client_tickets();
if ( FALSE === $utickets ){ return FALSE; }
foreach($utickets as $k => $ticket){
if ( TRUE === $this->isgranted($ticket) ){
//errors::raise("Ticket ".md5($ticket)." is granted", CORE_LOG_NOTICE, 'REALM');
if ( FALSE === $this->isactif($ticket) ){
//errors::raise("Ticket ".md5($ticket)." is NOT actif, so is passive eh", CORE_LOG_NOTICE, 'REALM');
$o[]=$ticket;
} else {
//errors::raise("Ticket ".md5($ticket)." is ACTIF", CORE_LOG_NOTICE, 'REALM');
}
} else {
//errors::raise("Ticket ".md5($ticket)." is NOT granted", CORE_LOG_NOTICE, 'REALM');
}
}
if ( isset($o) ){ return $o; }
return FALSE;
}
//-------------------------------------------------------------------------------------CLIENT TICKET
//-------------------------------------------------------------------------------------REALM ACTION
protected function newticket($key, $chaldata){
//check for offending maxticketbyclient
$limit = configs::get('realm', 'realm', Array($this->realm, 'maxchalticket'));
if ( $limit != 0 ){
$cur = $this->count_failed();
if ( $cur >= $limit ){
errors::raise("Host '".client::host()."' has exceeded maxchalticket rule", CORE_LOG_WARNING, 'REALM');
return 'realmsg_maxchalticket';
}
}
//check for offending maxticketbyhost
$limit = configs::get('realm', 'realm', Array($this->realm, 'maxticketbyhost'));
if ( $limit != 0 ){
$cur = $this->count_ticket();
if ( $cur >= $limit ){
errors::raise("Host '".client::host()."' has exceeded maxticketbyhost rule", CORE_LOG_WARNING, 'REALM');
return 'realmsg_maxticketbyhost';
}
}
if ( FALSE === $this->create_ticket($key, $chaldata) ){ return 'realmsg_nonewticket'; }
$utickets = $this->get_client_tickets();
$utickets[] = $key;
if ( FALSE === $this->set_client_tickets($utickets) ){ return 'realmsg_nocookset'; }
return TRUE;
}
protected function del_active_ticket(){
$ticket = $this->get_active_ticket();
if ( FALSE === $ticket ){ return FALSE; }
if ( FALSE === $this->remove_ticket($ticket) ){ return FALSE; }
$utickets = $this->get_client_tickets();
foreach($utickets as &$ticket){
if ( TRUE === $this->isgranted($ticket) ){
return $this->grant_ticket($this->ticket_uid($ticket), $ticket);
}
}
return TRUE;
}
protected function activate_ticket($ruid){
$utickets = $this->get_client_tickets();
if ( FALSE === $utickets ){ return 'realmsg_nocook'; }
$found=FALSE;
foreach($utickets as &$ticket){
if ( TRUE === $this->isgranted($ticket) ){
if ( $ruid == $this->ticket_uid($ticket) ){ $found=TRUE;break; }
}
}
if ( !$found ){ return 'realmsg_noticket'; }
//return $this->grant_ticket($ruid, $ticket);
$utickets = $this->get_client_tickets();
If ( !is_array($utickets) ){
errors::raise('No ticket set in cookies and you want to switchTo !!!', CORE_LOG_ERROR, 'REALM');
return 'realmsg_nocook';
}
$k = array_search($ticket, $utickets);
if ( FALSE === $k ){ return 'realmsg_noclienticket'; }
errors::raise('Ticket to grant found at K '.$k, CORE_LOG_NOTICE, 'REALM');
if ( FALSE === $this->mark_active($ticket, $ruid, $ticket) ){ return 'realmsg_noticketgrant'; }
foreach($utickets as $key => &$ticket ){
if ( $k !== $key AND $this->isgranted($ticket) ){
$this->mark_passive($ticket, $ticket);
}
}
return TRUE;
}
protected function grant_ticket($ruid, $ticket){
//check for offending maxticketbyuser
$limit = configs::get('realm', 'realm', Array($this->realm, 'maxticketbyuser'));
if ( $limit != 0 ){
$cur = $this->count_account($ruid);
if ( $cur >= $limit ){ return 'realmsg_maxticketbyuser'; }
}
$limit = configs::get('realm', 'realm', Array($this->realm, 'maxgranticket'));
if ( $limit != 0 ){
$cur = $this->count_granted();
if ( $cur >= $limit ){ return 'realmsg_maxgranticket'; }
}
$utickets = $this->get_client_tickets();
If ( !is_array($utickets) ){
errors::raise('No ticket set in cookies and you want to grant !!!', CORE_LOG_ERROR, 'REALM');
return 'realmsg_nocook';
}
$k = array_search($ticket, $utickets);
if ( FALSE === $k ){ return 'realmsg_noclienticket'; }
errors::raise('Ticket to grant found at K '.$k, CORE_LOG_NOTICE, 'REALM');
//update ticket and client cook with key. (credential stuff)
//$new = CORE::hash();
//$utickets[$k] = $new;
//if ( FALSE === $this->mark_active($ticket, $ruid, $new) ){ return 'realmsg_noticketgrant'; }
if ( FALSE === $this->mark_active($ticket, $ruid, $ticket) ){ return 'realmsg_noticketgrant'; }
if ( count($utickets) != 1 ){
foreach($utickets as $key => &$ticket ){
if ( $k !== $key AND $this->isgranted($ticket) ){
//$new = CORE::hash();
//$this->mark_passive($ticket, $new);
$this->mark_passive($ticket, $ticket);
//$ticket=$new;
}
}
}
//return $this->set_client_tickets($utickets);
return TRUE;
}
//-------------------------------------------------------------------------------------REALM ACTION
//----------------------------------------------------------------------DBCALL
public function isticket($ticket){
$arg=Array( 'ticket'=>md5($ticket), 'realm'=>md5($this->realm), 'host'=>md5(client::host()) );
return $this->dbquery($arg, __FUNCTION__);
}
public function ischallenged($ticket){
$arg=Array( 'ticket'=>md5($ticket), 'realm'=>md5($this->realm), 'host'=>md5(client::host()) );
return $this->dbquery($arg, __FUNCTION__);
}
public function isactif($ticket){
$arg=Array( 'ticket'=>md5($ticket), 'realm'=>md5($this->realm), 'host'=>md5(client::host()) );
return $this->dbquery($arg, __FUNCTION__);
}
public function isgranted($ticket){
$arg=Array( 'ticket'=>md5($ticket), 'realm'=>md5($this->realm), 'host'=>md5(client::host()) );
return $this->dbquery($arg, __FUNCTION__);
}
public function ticket_chaldata($ticket){
$arg=Array( 'ticket'=>md5($ticket), 'realm'=>md5($this->realm), 'host'=>md5(client::host()) );
return $this->dbquery($arg, __FUNCTION__, 'chaldata');
}
public function count_ticket(){
$arg=Array('host'=>md5(client::host()), 'realm'=>md5($this->realm) );
return $this->dbquery($arg, __FUNCTION__);
}
public function count_challenged(){
$arg=Array('host'=>md5(client::host()), 'realm'=>md5($this->realm) );
return $this->dbquery($arg, __FUNCTION__);
}
public function count_granted(){
$arg=Array('host'=>md5(client::host()), 'realm'=>md5($this->realm) );
return $this->dbquery($arg, __FUNCTION__);
}
public function count_failed(){ return $this->count_challenged() - $this->count_granted(); }
public function count_account($ruid){
$arg=Array( 'uid'=>$ruid, 'realm'=>md5($this->realm) );
return $this->dbquery($arg, __FUNCTION__);
}
protected function mark_challenged($ticket){
errors::raise('Marking Ticket '.md5($ticket).' challenged', CORE_LOG_NOTICE, 'REALM');
$arg=Array( 'ticket'=>md5($ticket), 'time'=>time(), 'realm'=>md5($this->realm), 'host'=>md5(client::host()) );
return $this->dbquery($arg, __FUNCTION__);
}
protected function ticket_uid($ticket){
$arg=Array( 'ticket'=>md5($ticket), 'realm'=>md5($this->realm), 'host'=>md5(client::host()) );
return $this->dbquery($arg, __FUNCTION__, 'uid');
return FALSE;
}
protected function create_time($ticket){
$arg=Array( 'realm'=>md5($this->realm), 'ticket'=>md5($ticket), 'host'=>md5(client::host()));
return $this->dbquery($arg, 'create_time', 'create');
}
protected function grant_time($ticket){
$arg=Array( 'realm'=>md5($this->realm), 'ticket'=>md5($ticket), 'host'=>md5(client::host()));
return $this->dbquery($arg, 'grant_time', 'chaldate');
}
protected function ticket_update($ticket){
$arg=Array( 'realm'=>md5($this->realm), 'ticket'=>md5($ticket), 'time'=>time(), 'host'=>md5(client::host()));
return $this->dbquery($arg, 'ticket_update');
}
private function create_ticket($ticket, $chaldata){
$arg=Array( 'ticket'=>md5($ticket), 'realm'=>md5($this->realm), 'host'=>md5(client::host()), 'chaldata'=>$chaldata, 'time'=>time() );
return $this->dbquery($arg, __FUNCTION__);
}
private function mark_passive($ticket, $new){
$arg=Array( 'ticket'=>md5($ticket), 'new'=>md5($new), 'time'=>time(), 'realm'=>md5($this->realm), 'host'=>md5(client::host()) );
return $this->dbquery($arg, __FUNCTION__);
}
private function mark_active($ticket, $ruid, $new){
$arg=Array( 'ticket'=>md5($ticket), 'uid'=>$ruid, 'time'=>time(), 'new'=>md5($new), 'realm'=>md5($this->realm), 'host'=>md5(client::host()) );
return $this->dbquery($arg, __FUNCTION__);
}
private function remove_ticket($ticket){
$arg=Array( 'ticket'=>md5($ticket), 'realm'=>md5($this->realm), 'host'=>md5(client::host()) );
return $this->dbquery($arg, 'drop_ticket');
}
private function drop_ticket($ticket){
$arg=Array( 'realm'=>md5($this->realm), 'ticket'=>$ticket );
return $this->dbquery($arg, 'drop_ticket');
}
//----------------------------------------------------------------------DBCALL
}
return TRUE;