othAuth component v0.5.4.5

By Othman ouahbi aka "CraZyLeGs"
the component part of othAuth

changes:
0.5.4.5:
- Fixed a bug that was introduced by lately, missing /
- Fixed a bug ( not really bug but oh well ) preventing othAuth from recovering a session from the cookie data in some situations
0.5.4:
- Fixed a bug in loginAttempts reported by PatDaMilla
- Added support for parameters sent via url in a traditional way, mainly for redirects, thanks to Ritesh.
- Probably some other minor bugs

0.5.3:
- Added compatibility with 1.2 redirections
0.5.2:
- Fixed a bug in getData
- Fixed a bug related to the '/' route
- Added lazy model loading support
- Hmm..maybe something else..eh
0.5.1:
added a minor modification to _passAuthData

Component Class:

Download code <?php 

/*
 * othAuth an auth system for cakePHP
 * comments, bug reports are welcome crazylegs AT gmail DOT com
 * @author Othman Ouahbi aka CraZyLeGs
 * @website: http://www.devmoz.com/blog/
 * @version 0.5.4.5
 * @license MIT
 * todo Router::url() in cakeAdmin and probably somewhere else
 */

class othAuthComponent extends Object
{
    
/**
* Constants to modify the behaviour of othAuth Component
*/
    // Form vars
    
var $user_login_var        'username';
    var 
$user_passw_var        'passwd';
    var 
$user_group_var        'group_id';
    var 
$user_cookie_var       'cookie';
    
    
// DB vars
    
var $user_table              'users';
    
    var 
$user_table_login      'username';
    var 
$user_table_passw      'passwd';
    var 
$user_table_gid        'group_id';
    var 
$user_table_active     'active';
    var 
$user_table_last_visit 'last_visit';
    var 
$auth_url_redirect_var 'from';
    var 
$show_auth_url_redirect_var true// decorate the url or not
    
var $user_model       'User';
    var 
$group_model      'Group';
    var 
$permission_model 'Permission';
    
    var 
$history_active   false;
    var 
$history_model    'UserHistory';
    
/*
     * Internals you don't normally need to edit those
     */
    
var $components    = array('Session','RequestHandler');
    var 
$controller    true;
    var 
$gid 1;
    var 
$redirect_page;
    var 
$hashkey       "mYpERsOnALhaSHkeY";
    var 
$auto_redirect true;
    
    var 
$login_page    '/users/login';
    var 
$logout_page   '';
    var 
$access_page   '/users/access_page';
    var 
$noaccess_page "/users/login"// session_flash, flash, back or a page url
    
    
var $mode 'oth';
    var 
$pass_crypt_method   'md5'// md5, sha1, crypt, crc32,callback
    
var $pass_crypt_callback null// function name
    
var $pass_crypt_callback_file ''// file where the function is declared ( in vendors )
     
    
    
var $cookie_active    true;
    var 
$cookie_lifetime '+1 day';
    
    
// asc : the most important group is the group with smallest value
    // desc: the most important group is the group with greatest value
    
var $gid_order 'asc'// asc desc
    
var $strict_gid_check true;
    
    var 
$kill_old_login true// when true, form can have another login with the same hash and del the old
    
    
var $allowedAssocUserModels       = array();
    var 
$allowedAssocGroupModels      = array();
    var 
$allowedAssocPermissionModels = array();
    
    var 
$allowedLoginChars = array('@','.','_');
    
    var 
$error_number 0;
    
    
    var 
$login_limit false// flag to toggle login attempts feature
    
    
var $login_attempts_model 'LoginAttempts';
    
    
    var 
$login_attempts_num 3;
    
    var 
$login_attempts_timeout 2// in minutes
    
    
var $login_locked_out '+1 day';
    
    
    
// startup() is kindof useless here because we init the component in beforeFilter,
    // and startup is called after that and before the action.
    // $this->othAuth->controller = &$this;
    
function startup(&$controller)
    {
       
//$this->controller = &$controller;
    
}
    
    function 
_getGidOp()
    {
        if(
$this->strict_gid_check)
        {
            return 
'';
        }else
        {
            return (
$this->gid_order == 'desc')? '>=' '<=';
        }
    }
    
    function 
_getHashOf($str)
    {
        switch(
$this->pass_crypt_method)
        {
            case 
'sha1':
                return (
$str == '')? '' sha1($str);
            break;
            case 
'crypt':
                return 
crypt($str);
            break;
            case 
'callback':
                
vendor($this->pass_crypt_callback_file);

                if(
function_exists($this->pass_crypt_callback))
                {
                    return 
call_user_func($this->pass_crypt_callback,$str);
                }
                return 
false;
            break;
            case 
'md5':
            default:
                return 
md5($str);
            break;
        }
    }
    function 
init($auth_config null
    {
        if(
is_array($auth_config) && !is_null($auth_config) && !empty($auth_config))
        {
            
            if(isset(
$auth_config['login_page']))
            {
                
$this->login_page $auth_config['login_page'];
            }
            
            if(isset(
$auth_config['logout_page']))
            {
                
$this->logout_page $auth_config['logout_page'];
            }
            
            if(isset(
$auth_config['access_page']))
            {
                
$this->access_page $auth_config['access_page'];
            }
            
            if(isset(
$auth_config['noaccess_page']))
            {
                
$this->noaccess_page $auth_config['noaccess_page'];
            }else
            {
                
$this->noaccess_page $this->login_page;
            }

            if(isset(
$auth_config['auto_redirect']))
            {
                
$this->auto_redirect = (boolean) $auth_config['auto_redirect'];
            }
            
            if(isset(
$auth_config['hashkey']))
            {
                
$this->hashkey $auth_config['hashkey'];
            }
            
            if(isset(
$auth_config['strict_gid_check']))
            {
                
$this->strict_gid_check = (boolean) $auth_config['strict_gid_check'];
            }
            
            if(isset(
$auth_config['mode']))
            {
                
$this->mode $auth_config['mode'];
            }

            if(isset(
$auth_config['allowModels']) && 
            
is_array($auth_config['allowModels']))
            {
                if(isset(
$auth_config['allowModels']['user']) && 
                
is_array($auth_config['allowModels']['user']))
                {
                    
$this->allowedAssocUserModels $auth_config['allowModels']['user'];
                }
                
                if(isset(
$auth_config['allowModels']['group']) && 
                
is_array($auth_config['allowModels']['group']))
                {
                    
$this->allowedAssocGroupModels $auth_config['allowModels']['group'];
                }
                
                if(isset(
$auth_config['allowModels']['permission']) && 
                
is_array($auth_config['allowModels']['permission']))
                {
                    
$this->allowedAssocPermissionModels $auth_config['allowModels']['permission'];
                }
            }
        }
        
        
// pass auth data to the view so it can be used by the helper
        
$this->_passAuthData();
    }
    
    
    function 
login($ap 1,$order ='asc'// username,password,group
   
{
       
       if(!
$this->_checkLoginAttempts())
       {
               return -
3// too many login attempts
       
}
       
       
$params null;
       if(!empty(
$this->controller->data[$this->user_model]))
       {
               
$params[$this->user_model] = $this->controller->data[$this->user_model];
       }        
        return 
$this->_login($params);
   }
   
   function 
_login($params,$ignore_cookie false)
   {
       switch (
$this->mode)
       {
               case 
'oth':
                       return 
$this->othLogin($params,$ignore_cookie);
                       break;
               case 
'nao':
                       return 
$this->naoLogin($params,$ignore_cookie);
                       break;
               case 
'acl':
                       return 
$this->aclLogin($params,$ignore_cookie);
                       break;
               default:
                       return 
$this->othLogin($params,$ignore_cookie);
                       break;
       }
   }
    
    function 
othLogin($params,$ignore_cookie=false// username,password,group
    
{
         
$params $params[$this->user_model];
         
         if(
$this->Session->valid() && $this->Session->check('othAuth.'.$this->hashkey))
         {
             if(!
$this->kill_old_login)
             {
                 return 
1;
             }
         } 

         if((
$params == null) || 
             !isset(
$params[$this->user_login_var]) || 
             !isset(
$params[$this->user_passw_var]))
         {
             return 
0;
         }
         
         
uses('sanitize');
         
$login Sanitize::paranoid($params[$this->user_login_var],$this->allowedLoginChars);
         
$passw Sanitize::paranoid($params[$this->user_passw_var]);
     
         if(
$login == "" || $passw == ""
         {
             return -
1;
         }
        
        if(!
$ignore_cookie)
        {
            
$passw $this->_getHashOf($passw);    
        }
        
        
$gid_check_op $this->_getGidOp();//($this->strict_gid_check)?'':'<=';         
         
$conditions = array();
         
         if(isset(
$params[$this->user_group_var]))
         {
             
$this->gid = (int) Sanitize::paranoid($params[$this->user_group_var]);
             
             
// FIX
            
if( $this->gid 1)
            {
                
$this->gid 1;
            }
            
$conditions[$this->user_model.'.'.$this->user_table_gid] = $gid_check_op.$this->gid;
         }

        
$conditions[$this->user_model.'.'.$this->user_table_login] = $login;
        
$conditions[$this->user_model.'.'.$this->user_table_passw] = $passw;
        
$conditions[$this->user_model.'.'.$this->user_table_active] = 1;
        
        
        
$UserModel = & $this->_createModel();
        
        
$row $UserModel->find($conditions);
        
        
        if( empty(
$row/* || $num_users != 1 */ )
        {
            
$this->_saveLoginAttempts();
            return -
2;
        }
        else
        {
            
$this->_deleteLoginAttempts();
            
            if(!
$ignore_cookie && 
                !empty(
$params[$this->user_cookie_var]) )
            {
                
$this->_saveCookie($row);
            }
        
            
$this->_saveSession($row);
            
            
// Update the last visit date to now
            
if(isset($this->user_table_last_visit))
            {    
                
$row[$this->user_model][$this->user_table_last_visit] = date('Y-m-d H:i:s');
                
$res $UserModel->save($row,true,array($this->user_table_last_visit)); 
            }
            
            
// 0.2.5 save history
            
if($this->history_active)
            {
                
$this->_addHistory($row);
            }
            
            if(
$this->auto_redirect == true)
            {
                
                if(!empty(
$row[$this->group_model]['redirect']))
                {
                    
$goto $row[$this->group_model]['redirect'];
                }
                else
                {
                    
$goto $this->access_page;
                }
                
$back false;//isset($this->controller->params['url']['url'][$this->auth_url_redirect_var]);
                
$this->redirect($goto,$back);
            }
            
            return 
1;
        }
         
    }
    
    function 
naoLogin($params,$ignore_cookie false// username,password,group
       
{
         
$params $params[$this->user_model];
         
         if(
$this->Session->valid() && $this->Session->check('othAuth.'.$this->hashkey))
         {
             if(!
$this->kill_old_login)
             {
                 return 
1;
             }
         }
         
         if(
$params == null || 
             !isset(
$params[$this->user_login_var]) || 
             !isset(
$params[$this->user_passw_var]))
         {
             return 
0;
         }
         
         
uses('sanitize');
         
$login Sanitize::paranoid($params[$this->user_login_var],$this->allowedLoginChars);
         
$passw Sanitize::paranoid($params[$this->user_passw_var]);
         if(isset(
$params[$this->user_group_var]))
         {
             
             
$this->gid = (int) Sanitize::paranoid($params[$this->user_group_var]);
            if( 
$this->gid 1)
            {
                
$this->gid 1;
            }
         }
     
         if(
$login == "" || $passw == ""
         {
             return -
1;
         }
         
        if(!
$ignore_cookie)
        {
            
$passw $this->_getHashOf($passw);    
        }
        
        
$conditions = array(
                            
"{$this->user_model}.".$this->user_table_login => "$login",
                            
"{$this->user_model}.".$this->user_table_passw => "$passw",
                            
"{$this->user_model}.".$this->user_table_active => 1);
        
        
$UserModel =& new $this->user_model;
        
$UserModel->unbindAll(array('belongsTo'=>array($this->group_model)));
        
$UserModel->recursive 2;

        
$UserModel->{$this->group_model}->unbindAll(array('hasAndBelongsToMany'=>array($this->permission_model)));
        
        
$row $UserModel->find($conditions);
        
        
$num_users = (int) $UserModel->findCount($conditions);

       
$gids = array();

       if(!empty(
$row[$this->group_model])){
               foreach (
$row[$this->group_model] as $group){
                       
$gids[] = $group['level'];
               }
       }

       if(
$this->strict_gid_check)
       {
               
$allowed in_array($this->gid,$gids);
       }
       else
       {
               
$allowed false;
               switch(
$this->gid_order)
               {
                   case 
'asc':
                       foreach(
$gids as $gid)
                       {
                           if(
$this->gid >= $gid)
                           {
                               
$allowed true;
                               break;
                           }
                       }
                   break;
                   case 
'desc':
                       foreach(
$gids as $gid)
                       {
                           if(
$this->gid >= $gid)
                           {
                               
$allowed true;
                               break;
                           }
                       }
                   break;
               }
       }

       if( empty(
$row) || $num_users != || !$allowed)
       {
               
$this->_saveLoginAttempts();
               return -
2;
       }
       else
       {
            
$this->_deleteLoginAttempts();
            
            if(!
$ignore_cookie && 
                !empty(
$params[$this->user_cookie_var]) )
            {
                
$this->_saveCookie($row);
            }
            
            
$this->_saveSession($row);
            
            
// Update the last visit date to now
            
if(isset($this->user_table_last_visit))
            {    
                
$row[$this->user_model][$this->user_table_last_visit] = date('Y-m-d H:i:s');
                
$res $UserModel->save($row,true,array($this->user_table_last_visit)); 
            }
            
            
// 0.2.5 save history
            
if($this->history_active)
            {
                
$this->_addHistory($row);
            }
            
            
$redirect_page $this->access_page;
            foreach(
$row[$this->group_model] as $grp)
            {
                if(
$grp['level'] == $this->gid)
                {
                    if(!empty(
$grp['redirect']))
                    {
                        
$redirect_page $grp['redirect'];
                    }
                }
            }
    
            
$this->redirect($redirect_page);
            
            return 
1;
       }

    }
    
    
// 0.2.5
    
function _addHistory(&$row)
    {
        
$data[$this->history_model]['username']  = $row[$this->user_model][$this->user_table_login];
        
$data[$this->history_model]['fullname']  = $row[$this->user_model]['fullname'];
        
$data[$this->history_model]['groupname'] = $row[$this->group_model]['name'];
        if(isset(
$row[$this->user_model][$this->user_table_last_visit]))
        {
            
$data[$this->history_model]['visitdate'] = $row[$this->user_model][$this->user_table_last_visit];
        }else
        {
            
$data[$this->history_model]['visitdate'] = date('Y-m-d H:i:s');
        }
        
        
loadModel($this->history_model);
        
$HistoryModel =& new $this->history_model;
        
$HistoryModel->save($data);
        
    }
    function 
_saveSession($row)
    {    
         
$login $row[$this->user_model][$this->user_table_login];
         
$passw $row[$this->user_model][$this->user_table_passw];
         
$gid   $row[$this->user_model][$this->user_table_gid];
         
$hk    $this->_getHashOf($this->hashkey.$login.$passw/*.$gid*/);
         
$row["{$this->user_model}"]['login_hash'] = $hk;
          
$row["{$this->user_model}"]['hashkey']    = $this->hashkey;
         
$this->Session->write('othAuth.'.$this->hashkey,$row);

    }
    
    
// null, true to delete the cookie
    
function _saveCookie($row,$del false)
    {
        if(
$this->cookie_active)
        {
            if(!
$del)
            {
                
$login  $row[$this->user_model][$this->user_table_login];
                
$passw  $row[$this->user_model][$this->user_table_passw];
                
                
$time   strtotime($this->cookie_lifetime);
                
$data   $login.'|'.$passw;
                
$data   serialize($data);
                
$data   $this->encrypt($data);
                
setcookie('othAuth',$data,$time,'/');
            }else
            {
                
setcookie('othAuth','',strtotime('-999 day'),'/');
            }
        }
    }
    
    function 
_readCookie()
    {
        
// does session exists
        
if($this->Session->valid() && $this->Session->check('othAuth.'.$this->hashkey))
        {
            return;
        }
        if(
$this->cookie_active && isset($_COOKIE['othAuth'])) {
            
            
$str $_COOKIE['othAuth'];
            if (
get_magic_quotes_gpc())
            {    
                
$str=stripslashes($str);
            }
                       
            
$str $this->decrypt($str);
              
            
$str = @unserialize($str);          
            
            list(
$login,$passw) = explode('|',$str);
            
//die($passw);
            
            
$data[$this->user_model][$this->user_login_var] = $login;
            
$data[$this->user_model][$this->user_passw_var] = $passw;
            
$redirect_old $this->auto_redirect;
            
$this->auto_redirect false;
            
$ret $this->_login($data,true);
            
$this->auto_redirect $redirect_old;
        }
    }
    
    
// delete attempts after a successful login
    
function _deleteLoginAttempts()
    {
        if(
$this->login_limit)
        {
            
$ip env('REMOTE_ADDR');
            
            
loadModel($this->login_attempts_model);
            
$Model = & new $this->login_attempts_model;
            
            
$Model->del($ip);
            
            if(
$this->cookie_active)
            {
                
setcookie('othAuth.login_attempts','',time() - 31536000,'/');
            }
        }