File: D:/HostingSpaces/PvdBoogaard/indoorski.nl/backup/oude-site/cms/api/class.auth.php
<?php
/**
* This file contains the iwp_auth base class and supporting global functions / definitions
*
* @version $Id$
*
*
* @package IWP
* @subpackage IWP_API
*/
define('IWP_MULTIPERM_MATCHTYPE_ALL', 0);
define('IWP_MULTIPERM_MATCHTYPE_ANY', 1);
/**
* Authentication Class
* This class is used by the api deal with user login sessions and checking
*
* @package IWP
* @subpackage IWP_API
*/
class iwp_auth extends iwp_engine {
/**
* Instance
* This static variable holds the current instance of this object being loaded.
* So using the getInstance function anywhere will return the very same instance.
*
* @var iwp_auth Instance
*/
public static $Instance;
/**
* A boolean value of whether or not the user is logged in. It is protected so it can't be changed externally.
*
* @var boolean
*/
private $isLoggedIn = false;
/**
* The ID integer of the current user loaded into this class
*
* @var integer
*/
protected $UserId = 0;
/**
* An array of the database row of the current user's data
*
* @var array
*/
protected $UserData = array();
/**
* An array of the groups that the currently loaded user is assigned to
*
* @var array
*/
private $arrGroupIds;
/**
* An array holding current user's permission data.
*
* @var Array
*/
protected $PermissionData;
/**
* Boolean flag to be set when permission data is loaded (faster than checking is_array on PermissionData)
*
* @var Boolean
*/
protected $PermissionDataLoaded = false;
/**
* An array holding current user's permission lookup cache data.
*
* @var Array
*/
protected $PermissionLookupCache;
/**
* Boolean flag to be set when permission data cache is loaded (faster than checking is_array on PermissionLookupCache)
*
* @var Boolean
*/
protected $PermissionLookupCacheLoaded = false;
/**
* getInstance
* This is a static function that sets up the class instance and stores it to the static variable. It will then return that instantiation in the future.
*
* @return iwp_auth Returns the instantiated object
**/
public static function getInstance(){
if(!isset(self::$Instance)){
self::$Instance = new self();
}
return self::$Instance;
}
/**
* This checks the current user's session information to see if they are logged in or not. It checks the session and cookies for a token.
*
* @return boolean True if the user is logged in, false if they aren't
*/
public function IsLoggedIn(){
if($this->isLoggedIn === true){
return true;
}
// check to see if they logged into the session
if(iwp_session::Exists('usertoken') && iwp_session::Exists('userid')) {
if($this->CheckUserToken(iwp_session::Get('usertoken'), iwp_session::Get('userid'))){
return true;
}
}
$cookiePrefix = iwp_config::Get('cookiePrefix');
if(isset($_COOKIE[$cookiePrefix . 'RememberMeIWP']) && $_COOKIE[$cookiePrefix . 'RememberMeIWP'] == 1){
if($this->CheckUserToken($_COOKIE[$cookiePrefix . 'RememberTokenIWP'], $_COOKIE[$cookiePrefix . 'RememberUserIdIWP'])){
return true;
}
}
return false;
}
/**
* This function checks the user's token against the database and setys the User data if its valid or returns false if its not
*
* @param string $token The user's current token
* @param integer $userid The user's current ID
*
* @return boolean True if the user's token is valid, false otherwise
*
* @see GenerateToken()
*/
protected function CheckUserToken($token, $userid)
{
$userid = (int)$userid;
if($userid < 1){
return false;
}
$result = $this->db->FetchQuery('select *,count(*) as usercount from ' . IWP_TABLE_USERS . ' where userid=' . $userid . ' group by userid');
if($result['usercount'] != 1){
return false;
}
$realToken = $this->GenerateToken($result['username'], $result['password'], $userid);
if($realToken === $token){
$this->isLoggedIn = true;
$this->UserId = $userid;
$this->UserData = $result;
return true;
}
return false;
}
/**
* This function generates a token hash from the username, password and userid of the user
*
* @param string $username The username of the user
* @param string $password The password of the user
* @param integer $useris The ID of the user
*
* @return string The hash token for the current user
*/
protected function GenerateToken($username, $password, $userid){
$token = sha1($username . '+' .$password . '+' . $userid);
return $token;
}
/**
* Callback function for usort() to sort permission data with deny flags first
*
* @TODO look into extending to sort cheaper flag checks to the top after sorting by deny/allow (if this is implemented, see about removing $sortForDeny)
* @param Array $a
* @param Array $b
* @return Integer
*/
public static function PermissionDataSortingFunction ($a, $b) {
if ($a['flag'] == $b['flag']){
return 0;
}elseif ($a['flag'] == 'deny'){
return -1;
}else{
return 1;
}
}
/**
* Saves the current user's permission lookup cache to the session
*
*/
public function SaveUserPermissionLookupCache ()
{
iwp_session::Set('userpermissionlookups', $this->PermissionLookupCache);
}
/**
* Checks for and, if exists, loads the current user's permission lookup cache from the session
*
*/
public function LoadUserPermissionLookupCache ()
{
if ($this->PermissionLookupCacheLoaded) {
return;
}
$this->PermissionLookupCache = array();
// check session to see if a permission lookup cache exists in the session, load it
if (iwp_session::Exists('userpermissionlookups')) {
$data = iwp_session::Get('userpermissionlookups');
//var_dump($data);
if (is_array($data)) {
$this->PermissionLookupCacheLoaded = true;
$this->PermissionLookupCache = $data;
return;
}
}
$this->PermissionLookupCacheLoaded = true;
}
/**
* Load permission data for current user from either the session or the database. Safe to call multiple times, will return early if data has already been loaded.
*
* @param Boolean $forceDatabase Set this to true to force a reload from the database.
*/
public function LoadUserPermissionData ($forceDatabase = false)
{
if (!$forceDatabase && $this->PermissionDataLoaded) {
return;
}
$this->PermissionData = array();
if (!($this->IsLoggedIn()) || $this->UserId < 1) {
return;
}
// check session to see if permission data has already been loaded from database and stored in session
if (!$forceDatabase && iwp_session::Exists('userpermissions')) {
$data = iwp_session::Get('userpermissions');
if (is_array($data)) {
$this->PermissionDataLoaded = true;
$this->PermissionData = $data;
return;
}
}
$groups = $this->GetGroupIds();
$sortForDeny = false; // flag this true if we push in a deny flag
foreach ($groups as $groupId) {
$permissions = iwp_groups::GetGroupPermissions($groupId, false);
foreach ($permissions as $permission) {
if (!$sortForDeny && $permission['flag'] == 'deny')
$sortForDeny = true;
array_push($this->PermissionData, $permission);
}
}
// for performance, sort deny flags first so they are matched earlier
if ($sortForDeny)
usort($this->PermissionData, array('iwp_auth', 'PermissionDataSortingFunction'));
$this->PermissionDataLoaded = true;
iwp_session::Set('userpermissions', $this->PermissionData);
}
/**
* Returns an array of group ids representing a list of groups the current user is in.
*
* @return Array Array of group ids
*/
public function GetGroupIds ()
{
if (is_array($this->arrGroupIds)) {
return $this->arrGroupIds;
}
$groups = iwp_user::GetUserGroups($this->UserData['userid']);
$this->arrGroupIds = array();
foreach ($groups as $group) {
array_push($this->arrGroupIds, $group['groupid']);
}
return $this->arrGroupIds;
}
/**
* Preloads data for the given user. Much of this (group list, permissions) has been moved to being load-on-demand instead.
*
* @return Boolean Returns true if the user is logged in, otherwise false.
*/
public function LoadUserData ()
{
if (!$this->IsLoggedIn() || $this->UserId < 1) {
return false;
}
return true;
}
/**
* Returns the current user's permission data (normally a protected member)
*
* @return Array
*/
public function GetPermissionData () {
$this->LoadUserPermissionData();
return $this->PermissionData;
}
/**
* Runs all permutations of the given parameters through HasPerm returning a match based on the $matchType parameter
*
* @see iwp_auth::HasPerm
*
* @param Integer $matchType IWP_MULTIPERM_MATCHTYPE_ALL (performs an AND match) or IWP_MULTIPERM_MATCHTYPE_ANY (performs an OR match)
* @param Array|String $scopeType
* @param Array|String $scopeName
* @param Array|String $scopeItem
* @param Array|Integer $itemId
* @param Array|Boolean $categoryCheck
* @return Boolean
*/
public function HasMultiPerm ($matchType, $scopeTypeSet, $scopeNameSet, $scopeItemSet, $itemIdSet = false, $categoryCheckSet = false) {
// convert all parameters to arrays if they are not already
if (!is_array($scopeTypeSet)) { $scopeTypeSet = array($scopeTypeSet); }
if (!is_array($scopeNameSet)) { $scopeNameSet = array($scopeNameSet); }
if (!is_array($scopeItemSet)) { $scopeItemSet = array($scopeItemSet); }
if (!is_array($itemIdSet)) { $itemIdSet = array($itemIdSet); }
if (!is_array($categoryCheckSet)) { $categoryCheckSet = array($categoryCheckSet); }
$matches = 0;
$checks = 0;
foreach ($scopeTypeSet as $scopeType) {
foreach ($scopeNameSet as $scopeName) {
foreach ($scopeItemSet as $scopeItem) {
foreach ($itemIdSet as $itemId) {
foreach ($categoryCheckSet as $categoryCheck) {
$checks++;
if ($this->HasPerm($scopeType, $scopeName, $scopeItem, $itemId, $categoryCheck)) {
$matches++;
if ($matchType == IWP_MULTIPERM_MATCHTYPE_ANY) {
// stop on first match if ANY
return true;
}
}
}
}
}
}
}
switch ($matchType) {
case IWP_MULTIPERM_MATCHTYPE_ALL:
return $matches == $checks;
break;
case IWP_MULTIPERM_MATCHTYPE_ANY:
return $matches > 0;
break;
}
}
/**
* Loads a content item using the given content id and returns the author id. This method exists in iwp_auth so that it may be overridden by iwp_test_auth for testing purposes.
*
* @param integer $contentId Content id of content item to load
* @return mixed The author id as an integer, or false if the content item could not be loaded.
*/
public function getContentAuthorId ($contentId) {
$content = new iwp_content();
if ($content->Load($itemId)) {
return intval($content->Get('author'));
}
return false;
}
/**
* Loads a content item using the given content id and returns a list of categories it is associated with. This method exists in iwp_auth so that it may be overridden by iwp_test_auth for testing purposes.
*
* @param integer $itemId Content id of content item to load
* @return mixed The list of category ids, or false if the content item could not be loaded.
*/
public function getContentCategories ($itemId) {
$content = new iwp_content();
if ($content->Load($itemId)) {
return $content->GetCategories();
}
return false;
}
/**
* Returns a permission option for the given scope. This method exists in iwp_auth so that it may be overridden by iwp_test_auth for testing purposes.
*
* @param string $scopeType
* @param string $scopeName
* @param string $scopeItem
* @return iwp_permissionoption
*/
public function getPermissionOption ($scopeType, $scopeName, $scopeItem) {
return iwp_permissionoption::GetPermissionOption($scopeType, $scopeName, $scopeItem);
}
/**
* Returns a ruling on whether the current user has permission to perform a given action on the given item.
*
* @param string $scopeType Scope type/group to check, eg. 'core'
* @param string $scopeName Scope name to check, eg. 'groups'
* @param string $scopeItem Scope item/action to check, eg. 'create'
* @param integer|string $itemId Optional, default false. Granular item id to check. Some scopes may have item ids as strings (eg. settings). May also specify a wildcard to match 'any'. If no item id is specified the 'all items' rule must be in effect for this to return true.
* @param boolean $categoryCheck Optional, default false. Set this to true to treat $itemId like a category, this only needs to be used for checking to see if an action can be performed on an item that will result in it being placed in said category (usually for content->create, content->edit)
* @return Boolean
*/
public function HasPerm ($scopeType, $scopeName, $scopeItem, $itemId = false, $categoryCheck = false) {
$debug = false;
// if we are in demo mode, ensure the PHP block is not available
if(IWP::$DemoMode && $scopeItem == 'addeditphpblock') {
return false;
}
// on-demand-load permission data, function takes care of duplicate calls
//$this->ClearPermissionData(); // debug call only; clear the cached permission data
$this->LoadUserPermissionData();
$this->LoadUserPermissionLookupCache();
// keep a cache for this execution incase the same check is performed twice
$cacheKey = $scopeType .'_'. $scopeName .'_'. $scopeItem .'_'. $itemId;
if (isset($this->PermissionLookupCache[$cacheKey])) {
return $this->PermissionLookupCache[$cacheKey];
}
if ($debug) {
echo "<br />\n\nHasPerm ($scopeType, $scopeName, $scopeItem, $itemId, $categoryCheck)";
}
$result = false;
$isContentModule = $scopeType == 'content' && strpos($scopeItem, '_') !== false;
if ($isContentModule) {
// when asking about permissions on content, trap questions against module fields
// so that we can look for 'full' permissions on the module itself and apply them to content
$contentModuleName = substr($scopeItem, 0, strpos($scopeItem, '_'));
}
foreach ($this->PermissionData as $perm) {
$scopeMatch = false;
$granularMatch = false;
$flag = $perm['flag'];
// first match the scope (ie. user editing, content deleting etc.)
if ($perm['scopetype'] == 'core' && $perm['scopename'] == 'root') {
$scopeMatch = true; // root permission matches all other permissions
} else if ($isContentModule && $perm['scopetype'] == 'sitemodules' && $perm['scopename'] == $contentModuleName && $perm['scopeitem'] == 'full') {
$scopeMatch = true; // let checks for content, ?, {modulename}_{permission} also match sitemodules, {modulename}, 'full'
} else if (
$perm['scopetype'] == $scopeType
&& (
$perm['scopename'] == $scopeName // explicit scope match
|| ($perm['scopetype'] == 'content' && $perm['scopename'] == '__all') // special __all scope match for contenttypes
// @TODO : Look at inserting check for categorised content types here
|| ($scopeName == '*' && $flag != 'deny') // wildcard scope match, but do not match deny flags for wildcard matches
|| ($perm['scopename'] == 'root' && $flag != 'deny')
) && (
$scopeItem == '*' // match any scope item
|| $perm['scopeitem'] == $scopeItem // explicit scope match
|| //(
$perm['scopeitem'] == 'full' // defined scope item is 'full' control, and should match all other items
//&& (
// $itemId // but only if there's an itemid in the check (so it can be denied later in the item/cat checked)
// //|| (!$itemId && $scopeItem == 'view') // or if the request is to 'view', but no itemid is given (to allow dropdowns to function)
// //|| (!$itemId && !in_array('specific', $perm['granularity'])) //
//)
//)
)
) {
$scopeMatch = true;
}
// @TODO : Look at inserting check for categorised content types here
if ($scopeMatch) {
if ($itemId === false) {
// item id not specified in the check, so the request is to check permission on "all" items
if (count($perm['granularity']) == 0 || in_array('all', $perm['granularity'])) {
$granularMatch = true;
}
} else if ($itemId == '*') {
// if item is defined as as *, it's looking for any match, and the fact that we have a scopeMatch means 'any' has been satisfied
$granularMatch = true;
} else {
// load permission options data so we can check if granularity is allowed on this scope
// note that the wildcard check is handled above, not here
$option = $this->getPermissionOption($perm['scopetype'], $perm['scopename'], $perm['scopeitem']);
if ($option && ($option->Granular || $option->GranularOwn)) {
// check for a match on granularity
foreach ($perm['granularity'] as $granularityCode) {
switch ($granularityCode) {
case 'all':
// permission covers all items in this scope
// no matter what the requested itemid is, allow it
$granularMatch = true;
break;
case 'own':
if ($option->GranularOwn) {
// check only required for 'content' and 'user' types, as they are the only records that users can have ownership over
switch ($perm['scopetype']) {
case 'content':
// load content item, check if author is current user
$contentAuthorId = $this->getContentAuthorId((int)$itemId);
if ($this->UserId === $contentAuthorId) {
$granularMatch = true;
}
break;
case 'core':
if ($scopeName == 'user') {
// check if user id matches current user id (this defines 'own' user)
if ($itemId === $this->UserId) {
$granularMatch = true;
}
}
break;
}
}
break;
case 'specific':
// the permission applies to a specific set of items or categories
if (!$categoryCheck && is_array($perm['itemcsv']) && count($perm['itemcsv']) && in_array($itemId, $perm['itemcsv'])) {
// the query is against a specific item, not a category, and it exists in the defined itemcsv list - resulting in a positive match
$granularMatch = true;
} else if ($perm['scopetype'] == 'content' && is_array($perm['catcsv']) && count($perm['catcsv'])) {
// check catcsv as necessary, only needed if it's the content scope as it's the only type of record which can be associated with categories
if ($categoryCheck) {
// special condition for content checking, assume $itemId is a category
if ($itemId == '*') {
// the permission check is asking to see if we can perform (action) in at least one category
// the fact that catcsv has a size > 0 means we can match this
$granularMatch = true;
break;
} else {
// $itemId is a category id that we should check against the supplied list of categories in the permission record
if (in_array($itemId, $perm['catcsv'])) {
$granularMatch = true;
break;
}
}
} else {
$categories = $this->getContentCategories((int)$itemId);
if (is_array($categories) && count($categories)) {
foreach ($categories as $category) {
if (in_array($category, $perm['catcsv'])) {
$granularMatch = true;
break;
}
}
}
}
}
break;
}
}
} else {
$granularMatch = true;
}
}
}
$match = ($scopeMatch && $granularMatch);
if ($debug) {
echo "\n(flag?$flag perm.scopetype={$perm['scopetype']} perm.scopeitem={$perm['scopeitem']} perm.granularity=" . implode(',', $perm['granularity']) . " scopeMatch?$scopeMatch granularMatch?$granularMatch) ";
}
if ($match) {
if ($flag == 'deny') {
$result = false;
} else {
$result = true;
}
// we break on first match because deny flags are checked first, meaning that any allow flag match has already passed all deny flag checks already
// load permission options data so we can check if granularity is allowed on this scope
// note that the wildcard check is handled above, not here
$option = iwp_permissionoption::GetPermissionOption($perm['scopetype'], $perm['scopename'], $perm['scopeitem']);
if ($flag == 'deny' && $itemId == '*' && $option->Granular && !in_array('all', $perm['granularity'])) {
// don't stop yet if we're checking for 'any' permission (using an asterisk) and this is a deny flag and the granularity is not 'all'
// keep checking for a specific allow match
} else {
break;
}
}
}
$this->PermissionLookupCache[$cacheKey] = $result;
iwp_session::Set('userpermissionlookups', $this->PermissionLookupCache);
if ($debug) {
echo "\n(result?$result) ";
}
return $result;
}
/**
* Returns an item from the UserData array.
*
* @param String $field The array key of the value to fetch from the UserData array.
* @return Mixed The value contained at the specified key in the UserData array.
*/
public function GetUserInfo ($field)
{
if(isset($this->UserData[$field])){
return $this->UserData[$field];
}else{
return false;
}
}
/**
* Gets the user id for the current user.
*
* @return Int The user id for the current user.
*/
public function GetUserId(){
return (int)$this->UserId;
}
/**
* Constructor method for this class.
*
* @return iwp_auth
*/
public function __construct ()
{
}
/**
* Clears the current user's permission data, both in-process and session
*
*/
public function ClearPermissionData ()
{
$this->PermissionData = null;
iwp_session::Kill('userpermissions');
$this->PermissionDataLoaded = false;
$this->PermissionLookupCache = null;
iwp_session::Kill('userpermissionlookups');
$this->PermissionLookupCacheLoaded = false;
}
}
/**
* Helper function for template usage.
*
* @see iwp_auth::HasPerm
*/
function iwp_HasPerm ($scopeType, $scopeName, $scopeItem, $itemId = false, $categoryCheck = false)
{
return iwp_auth::getInstance()->HasPerm($scopeType, $scopeName, $scopeItem, $itemId, $categoryCheck);
}
/**
* Helper function for template usage.
*
* @see iwp_auth::HasMultiPerm
*/
function iwp_HasMultiPerm ($matchType, $scopeTypeSet, $scopeNameSet, $scopeItemSet, $itemIdSet = false, $categoryCheckSet = false)
{
return iwp_auth::getInstance()->HasMultiPerm($matchType, $scopeTypeSet, $scopeNameSet, $scopeItemSet, $itemIdSet, $categoryCheckSet);
}
/**
* Helper function for template usage.
*
* @see iwp_user::HasAnyEditPermission
*/
function iwp_User_HasAnyEditPermission ($userId) {
return iwp_user::HasAnyEditPermission($userId);
}