<?php
/***************************************************************************\
* End Config *
\***************************************************************************/
class AuthComponent extends Component
{
var $controller = true;
var $currentUser;
var $User, $Role, $Controller,$Sanitize;
function initialize( &$controller)
{
//making sure we don't initialize twice.
if(empty($this->Controller))
{
$this->Controller =& $controller; //set callbacks for data and stuffs
if (! class_exists('Sanitize'))
{
Uses('Sanitize');
}
$this->Sanitize = new Sanitize();
loadModel('User');
loadModel('Role');
$this->User = new User;
$this->Role = new Role;
//set up our info for the current user
if (!$this->Controller->Session->check(RBAC_KEY))
{
$this->setUserSession(RBAC_GUEST_NAME, RBAC_GUEST_ID, RBAC_GUEST_LOGIN,$this->buildPermissionList(RBAC_GUEST_ID));
}
$this->currentUser = $this->Controller->Session->read(RBAC_KEY);
}
}
/***************************************************************************\
* log in/log out related methods *
\***************************************************************************/
/**
* Destroys the session which has the effect of logging the user out.
* @return
*/
function logout()
{
$this->Controller->Session->destroy();
}
/**
* Default login() action, checks passwords and populates the session with proper data
* Assumes that it is called from the UsersController or another controller with similar data structure.
* Uses the calling controllers data
* @returns No return, if it works it redirects, otherwise view should be shown.
*/
function login()
{
$supp = null; //db user info for attempted log in
$r_salt = null; //stored salt
$r_hash = null; //stored hash
if(!isset($this->Controller->data['User']['password']))
{
$this->Controller->PulseError('Please fill out all required fields.','AuthController : s02');
}
else
{
$this->Sanitize->paranoid($this->Controller->data['User']['user_name']);
$supp = $this->User->checkUserPass($this->Controller->data['User']['user_name'],
$this->Controller->data['User']['password']);
if ($supp === false ) //no user by that name most likely
{
$this->Controller->PulseError('User/Password error','AuthController :s05');
}
elseif($supp['User']['active'] === false)
{
$this->Controller->PulseError('User has been locked, please contact your Administrator','AuthController : s01');
}
else
{
//everything verifies, Storing user info and redirecting
$supp = $supp['User'];
$perms = $this->buildPermissionList($supp['id']);
//check to see if there is a role assigned to every logged in user
// usefull for things like.. I donno... logging out? :P
if (defined('RBAC_LOGIN_ROLE'))
{
$this->parseRolePermissions($this->Role->find(array('Role.name'=>RBAC_LOGIN_ROLE),null,null,2), $perms);
}
//set the session info.
if ($this->setUserSession($supp['first_name']." ".$supp['last_name'], $supp['id'], $supp['user_name'],$perms))
{
$redirect = $this->Controller->Session->read('loginRedirect');
if (empty($redirect))
{
$redirect = '/';
}
elseif(substr($redirect,0,1) !== '/')
{
$redirect = '/'. $redirect;
}
$this->Controller->flash('Log in Successful', $redirect);
}
else
{
$this->Controller->PulseError('Failed to create session.','AuthController : s03');
}
}
}
}
/**
* Writes Session info a user
* @return
* @param $user_name Object
* @param $user_id Object
* @param $user_login Object
* @param $permissions Object
*/
private function setUserSession($user_name,$user_id,$user_login,$permissions)
{
//everything verifies, Storing user info and redirecting
$user_array = array('User'=>array(
'login' =>$user_login,
'id' =>$user_id,
'name' =>$user_name),
'Permissions' => $permissions);
return $this->Controller->Session->write(RBAC_KEY, $user_array);
}
/**
* Checks if a user is an admin. Looks at the Permission structure in $this->currentUser
* @return Bool
* @param $user_id
*/
public function isAdmin($user_id = null)
{
return (isset($this->currentUser['Permissions']['is_rbac_admin']) && $this->currentUser['Permissions']['is_rbac_admin'] == true) ;
}
/**
* sets a page to be redirected to on login.
* @return
*/
public function setLoginRedirect()
{
if(isset($this->params['url']['url']))
{
$this->session->write('loginRedirect',$this->params['url']['url']);
}
}
/***************************************************************************\
* Access level control and reporting functions *
\***************************************************************************/
/**
* Get a data item about the user
* @return {String} The info, or null if not set.
* @param {String} $info One of these three 'login', 'id', 'name'
*/
public function getCurrent($info)
{
return isset($this->currentUser['User'][$info]) ? $this->currentUser['User'][$info] : NULL;
}
/**
* Compares two lists of roles, four possible return values:
* 1 : $list1 contains every group in $list2, and one or more additionial
* 0 : $list1 and $list2 have the exact same contents
* -1 : $list2 contains at least one member that $list1 does not
* -2 : one of the parameters is not an array
* @return int
* @param {Array} $list1 List of groups
* @param $list2 List of groups
*/
public function compareRoles(array $list1, $list2)
{
$retVal = -2;
if(is_array($list1) && is_array($list2))
{
//get our difference
$diff = array_diff($list1,$list2);
if (!empty($diff))
{
$retVal = 1; //assuming 1, if loop ends without altering, this is valid.
array_flip($list1); //flip list1 for easy checking...
$currentVal = array_pop($diff); //priming read...
while($currentVal !== null && $retVal !== -1)
{
if ( !isset($list1[$currentVal]))
{
$retVal = -1;
}
$currentVal = array_pop($diff);
}
}
else
{
$retVal = 0;
}
}
return $retVal;
//Todo finish me (hint, array_diff() )
}
/**
* Adds the permissions for a transient role to the current permission set.
* This only lasts through the page view.
* @return
* @param $role_name Object
*/
public function addTransRole($role_name)
{
$this->parseRolePermissions($this->Role->find(array('Role.name'=>$role_name),null,null,2), $this->currentUser['Permissions']);
}
public function buildRoleList()
{
//If no User id specified, assume current user.
if($user_id == null)
{
$user_id = $this->getCurrent('id');
}
$this->User->id = $user_id;
}
/**
* Builds the permission list for user specified with $user_id
* @return
* @param $user_id Object
*/
public function buildPermissionList($user_id = null)
{
$permissions = array(); //the array we are going to return
$permTree = false; //our buffer for the find()
//If no User id specified, assume current user.
if($user_id == null)
{
$user_id = $this->getCurrent('id');
}
$this->User->id = $user_id;
//make sure that the user exists
if($this->User->exists())
{
//unbind all User associations except for the Role Assoc.
$this->User->unbindAll(array('hasAndBelongsToMany'=>array('Role')));
$permissions = $this->parseUserPermissions($this->User->find("User.id = '".$user_id."'",null,null,3));
}
return $permissions;
}
/**
* Parses the results of a User array passed to it from a find()
* @return returns a permissions array in the format of 'ControllerName:Action'=> true/false
* @param {Array} $user_info User info array from find()
*/
function parseUserPermissions($user_info)
{
$permissions = array();
//short cut to check for admin role
if(array_search(RBAC_ADMIN_ROLE,Set::extract($user_info,'Role.{n}.name') ) !== false)
{
$user_info['Role'] = array();
//hey lookie, this is an admin! lets fetch all roles! (except for Guest role)
$this->User->Role->unbindAll(array('hasMany'=>array('Permission')));
$this->User->Role->recursive = 2;
foreach($this->User->Role->findAll(array('Not' => array('name'=>RBAC_GUEST_ROLE))) as $key => $curRole)
{
//need to fix the structure of the array to match expected
$user_info['Role'][$key] = $curRole['Role'];
$user_info['Role'][$key]['Permission'] = $curRole['Permission'];
}
$permissions['is_rbac_admin'] = 'yes';
}
if ( is_array($user_info) && !empty($user_info['Role']))
{
foreach($user_info['Role'] as $role)
{
$this->parseRolePermissions($role, $permissions);
}
}
return $permissions;
}
/**
* Add the permissions of a Role that have been returned from a find() to $permissions.
* @return {Array} returns the the permissions after the role has been applied.
* @param {Array} $permTree Array holding the Roles for the User
* @param {Array} $permissions Array holding the current permissions list
*/
function parseRolePermissions($role, &$permissions)
{
//does this role have any permissions?
if (!empty($role['Permission']))
{
foreach($role['Permission'] as $currPerm)
{
//if it's not set, create empty permission for simplicities sake
if (!isset($permissions[$currPerm['Action']['controller']][$currPerm['Action']['action_name']]))
{
$permissions[$currPerm['Action']['controller']][$currPerm['Action']['action_name']] = null; //
}
//Our target permission, by reference
$tPerm =& $permissions[$currPerm['Action']['controller']][$currPerm['Action']['action_name']];
//if the permission has been denied previously, then skip
if ($tPerm !== false)
{
//if both are set, cannot_exec overrides can.
if ($currPerm['cannot_exec'] === 1)
{
$tPerm['execute'] = false; //denied! I say NO!
}
elseif($currPerm['can_exec'] === 1)
{
$tPerm['execute'] = true; //ok for now.
$tPerm['menu_category'] = $currPerm['Action']['menu_category'];
$tPerm['menu_desc'] = $currPerm['Action']['menu_desc'];
$tPerm['alternate_url'] = $currPerm['Action']['alternate_url'];
}
}
}
}
}
/**
* Checks to see if a user can execute the specified action. If $user_id is null it assumes
* current user, and checks cached permissions.
* @return
* @param $controller Object
* @param $action Object
* @param $user_id Object[optional]
*/
function canExecute($controller = null, $action = null, $user_id = null)
{
$retVal = false;
//defaults to current controller and action if no controller/action is specified
if ($controller == null)
{
$controller = $this->Controller->name;
}
if ($action == NULL )
{
if (!isset($this->Controller->params['action']))
{
pr($this->Controller->params);
}
$action = $this->Controller->params['action'];
}
if ($user_id === null)
{
$perm = $this->currentUser['Permissions'];
}
else
{
$perm = $this->BuildPermissionList($user_id);
}
//if it's set and is true, then return true, otherwise it's denied
if ((isset ($perm[$controller][$action]['execute']) && $perm[$controller][$action]['execute'] == 1) || $this->isAdmin())
{
$retVal = true;
}
return $retVal;
}
}
?>
<?php
class AuthHelper extends Helper
{
var $helpers = array('Session','Html');
var $info = null;
/**
* Loads $this->info with the current user data from the session (this will not show dynamic roles...)
* @return
*/
private function setInfo()
{
if (empty($this->info))
{
$this->info = $this->Session->read('AuthInfo');
}
}
/**
* Checks if a user is an admin. Looks at the Permission structure in $this->info
* @return Bool
* @param $user_id
*/
public function isAdmin($user_id = null)
{
$this->setInfo();
return (isset($this->info['Permissions']['is_pulse_admin']) && $this->info['Permissions']['is_pulse_admin'] == true) ;
}
/**
* Get a file the User id of the current user
* @return User_id of the current user, or false if not set
*/
public function getCurrent($info)
{
$this->setInfo();
return isset($this->info['User'][$info]) ? $this->output($this->info['User'][$info]) : $this->output(NULL);
}
/**
* Checks to see if the current user is guest or not set, and returns false on either one
* @return true if user is not guest, false if user is guest or logged in.
*/
public function getLoginStatus()
{
$this->setInfo();
return (!empty($this->info['User']['login']) && $this->info['User']['login'] != 'guest');
}
/**
* Outputs a <dl class="prettylist"> with the permissions and their allowed/dissalowed state
* @return {Mixed} a string with the <dl> or null on error
* @param {Array} $perms Permission list to parse
*/
public function parsePermissions($permList= array())
{
$retVal = null;
if($permList === array())
{
$this->setInfo();
$permList = $this->info;
}
if (is_array($permList))
{
$retVal = '<dl class="prettylist"><dt class="title">Permission</dt><dd class="title">value</dd>';
foreach ($permList as $controller => $action)
{
if( is_array($action) )
{
foreach ($action as $actionName => $perm)
{
$retVal .= "\n<dt>".$controller." : ".$actionName."</dt><dd>";
if ($perm['execute'] == 1) //are they authorized?
{
$retVal .= "Allow";
}
else
{
$retVal .= "Deny";
}
$retVal .= "</dd>";
}
}
}
}
return $this->output($retVal);
}
public function getCurrentMenu()
{
// $retVal = '<ul id="mainMenu">'; //our returned string, init with the start of the list //uncommment for dropdown
$retVal = '<ul class="menusub">'; //our returned string, init with the start of the list //uncommment for dropdown
$menuBuff = array(); //menu buffer, filled after parsing the array.
$this->setInfo(); //need to make sure our info has been set. It will, but good habits..
//cycle through the menu items and get every permission with associated menu entries
foreach ($this->info['Permissions'] as $controller => $action)
{
if(is_array($action)) //is_rbac_admin will freak this out.
{
foreach ($action as $actionName => $perm)
{
if ($perm['execute'] == 1 && !empty($perm['menu_category']) && !empty($perm['menu_desc'])) //are they authorized, and is this a menu item?
{
$menuBuff[$perm['menu_category']][] = array(
'description'=>$perm['menu_desc'],
'controller'=>$controller,
'action'=>$actionName,
'alt_url'=>$perm['alternate_url']);
}
}
}
}
//Build our menu. It might be nice to have some way to sort them, but I'll go there later ;p
foreach ($menuBuff as $catName => $catItems )
{
$retVal .= "<li>".$catName."\n<ul>";
foreach($catItems as $item)
{
if(!empty($item['alt_url']))
{
$link = $this->Html->link($item['description'], $item['alt_url']);
}
else
{
$link = $this->Html->link($item['description'], '/'.$item['controller'].'/'.$item['action'].'/');
}
$retVal .= "\n<li>".$link."</li>";
}
$retVal .= "\n</ul></li>";
}
$retVal .= "</ul>"; //close out our menu list and return
return $this->output($retVal);
}
}
?>