You've already forked joomla_test
							
							
		
			
				
	
	
		
			571 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			571 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * @package     Joomla.Platform
 | |
|  * @subpackage  Access
 | |
|  *
 | |
|  * @copyright   Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
 | |
|  * @license     GNU General Public License version 2 or later; see LICENSE
 | |
|  */
 | |
| 
 | |
| defined('JPATH_PLATFORM') or die;
 | |
| 
 | |
| jimport('joomla.utilities.arrayhelper');
 | |
| 
 | |
| /**
 | |
|  * Class that handles all access authorisation routines.
 | |
|  *
 | |
|  * @package     Joomla.Platform
 | |
|  * @subpackage  Access
 | |
|  * @since       11.1
 | |
|  */
 | |
| class JAccess
 | |
| {
 | |
| 	/**
 | |
| 	 * Array of view levels
 | |
| 	 *
 | |
| 	 * @var    array
 | |
| 	 * @since  11.1
 | |
| 	 */
 | |
| 	protected static $viewLevels = array();
 | |
| 
 | |
| 	/**
 | |
| 	 * Array of rules for the asset
 | |
| 	 *
 | |
| 	 * @var    array
 | |
| 	 * @since  11.1
 | |
| 	 */
 | |
| 	protected static $assetRules = array();
 | |
| 
 | |
| 	/**
 | |
| 	 * Array of user groups.
 | |
| 	 *
 | |
| 	 * @var    array
 | |
| 	 * @since  11.1
 | |
| 	 */
 | |
| 	protected static $userGroups = array();
 | |
| 
 | |
| 	/**
 | |
| 	 * Array of user group paths.
 | |
| 	 *
 | |
| 	 * @var    array
 | |
| 	 * @since  11.1
 | |
| 	 */
 | |
| 	protected static $userGroupPaths = array();
 | |
| 
 | |
| 	/**
 | |
| 	 * Array of cached groups by user.
 | |
| 	 *
 | |
| 	 * @var    array
 | |
| 	 * @since  11.1
 | |
| 	 */
 | |
| 	protected static $groupsByUser = array();
 | |
| 
 | |
| 	/**
 | |
| 	 * Method for clearing static caches.
 | |
| 	 *
 | |
| 	 * @return  void
 | |
| 	 *
 | |
| 	 * @since   11.3
 | |
| 	 */
 | |
| 	public static function clearStatics()
 | |
| 	{
 | |
| 		self::$viewLevels = array();
 | |
| 		self::$assetRules = array();
 | |
| 		self::$userGroups = array();
 | |
| 		self::$userGroupPaths = array();
 | |
| 		self::$groupsByUser = array();
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Method to check if a user is authorised to perform an action, optionally on an asset.
 | |
| 	 *
 | |
| 	 * @param   integer  $userId  Id of the user for which to check authorisation.
 | |
| 	 * @param   string   $action  The name of the action to authorise.
 | |
| 	 * @param   mixed    $asset   Integer asset id or the name of the asset as a string.  Defaults to the global asset node.
 | |
| 	 *
 | |
| 	 * @return  boolean  True if authorised.
 | |
| 	 *
 | |
| 	 * @since   11.1
 | |
| 	 */
 | |
| 	public static function check($userId, $action, $asset = null)
 | |
| 	{
 | |
| 		// Sanitise inputs.
 | |
| 		$userId = (int) $userId;
 | |
| 
 | |
| 		$action = strtolower(preg_replace('#[\s\-]+#', '.', trim($action)));
 | |
| 		$asset = strtolower(preg_replace('#[\s\-]+#', '.', trim($asset)));
 | |
| 
 | |
| 		// Default to the root asset node.
 | |
| 		if (empty($asset))
 | |
| 		{
 | |
| 			$db = JFactory::getDbo();
 | |
| 			$assets = JTable::getInstance('Asset', 'JTable', array('dbo' => $db));
 | |
| 			$rootId = $assets->getRootId();
 | |
| 			$asset = $rootId;
 | |
| 		}
 | |
| 
 | |
| 		// Get the rules for the asset recursively to root if not already retrieved.
 | |
| 		if (empty(self::$assetRules[$asset]))
 | |
| 		{
 | |
| 			self::$assetRules[$asset] = self::getAssetRules($asset, true);
 | |
| 		}
 | |
| 
 | |
| 		// Get all groups against which the user is mapped.
 | |
| 		$identities = self::getGroupsByUser($userId);
 | |
| 		array_unshift($identities, $userId * -1);
 | |
| 
 | |
| 		return self::$assetRules[$asset]->allow($action, $identities);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Method to check if a group is authorised to perform an action, optionally on an asset.
 | |
| 	 *
 | |
| 	 * @param   integer  $groupId  The path to the group for which to check authorisation.
 | |
| 	 * @param   string   $action   The name of the action to authorise.
 | |
| 	 * @param   mixed    $asset    Integer asset id or the name of the asset as a string.  Defaults to the global asset node.
 | |
| 	 *
 | |
| 	 * @return  boolean  True if authorised.
 | |
| 	 *
 | |
| 	 * @since   11.1
 | |
| 	 */
 | |
| 	public static function checkGroup($groupId, $action, $asset = null)
 | |
| 	{
 | |
| 		// Sanitize inputs.
 | |
| 		$groupId = (int) $groupId;
 | |
| 		$action = strtolower(preg_replace('#[\s\-]+#', '.', trim($action)));
 | |
| 		$asset = strtolower(preg_replace('#[\s\-]+#', '.', trim($asset)));
 | |
| 
 | |
| 		// Get group path for group
 | |
| 		$groupPath = self::getGroupPath($groupId);
 | |
| 
 | |
| 		// Default to the root asset node.
 | |
| 		if (empty($asset))
 | |
| 		{
 | |
| 			// TODO: $rootId doesn't seem to be used!
 | |
| 			$db = JFactory::getDbo();
 | |
| 			$assets = JTable::getInstance('Asset', 'JTable', array('dbo' => $db));
 | |
| 			$rootId = $assets->getRootId();
 | |
| 		}
 | |
| 
 | |
| 		// Get the rules for the asset recursively to root if not already retrieved.
 | |
| 		if (empty(self::$assetRules[$asset]))
 | |
| 		{
 | |
| 			self::$assetRules[$asset] = self::getAssetRules($asset, true);
 | |
| 		}
 | |
| 
 | |
| 		return self::$assetRules[$asset]->allow($action, $groupPath);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Gets the parent groups that a leaf group belongs to in its branch back to the root of the tree
 | |
| 	 * (including the leaf group id).
 | |
| 	 *
 | |
| 	 * @param   mixed  $groupId  An integer or array of integers representing the identities to check.
 | |
| 	 *
 | |
| 	 * @return  mixed  True if allowed, false for an explicit deny, null for an implicit deny.
 | |
| 	 *
 | |
| 	 * @since   11.1
 | |
| 	 */
 | |
| 	protected static function getGroupPath($groupId)
 | |
| 	{
 | |
| 		// Preload all groups
 | |
| 		if (empty(self::$userGroups))
 | |
| 		{
 | |
| 			$db = JFactory::getDbo();
 | |
| 			$query = $db->getQuery(true)
 | |
| 				->select('parent.id, parent.lft, parent.rgt')
 | |
| 				->from('#__usergroups AS parent')
 | |
| 				->order('parent.lft');
 | |
| 			$db->setQuery($query);
 | |
| 			self::$userGroups = $db->loadObjectList('id');
 | |
| 		}
 | |
| 
 | |
| 		// Make sure groupId is valid
 | |
| 		if (!array_key_exists($groupId, self::$userGroups))
 | |
| 		{
 | |
| 			return array();
 | |
| 		}
 | |
| 
 | |
| 		// Get parent groups and leaf group
 | |
| 		if (!isset(self::$userGroupPaths[$groupId]))
 | |
| 		{
 | |
| 			self::$userGroupPaths[$groupId] = array();
 | |
| 
 | |
| 			foreach (self::$userGroups as $group)
 | |
| 			{
 | |
| 				if ($group->lft <= self::$userGroups[$groupId]->lft && $group->rgt >= self::$userGroups[$groupId]->rgt)
 | |
| 				{
 | |
| 					self::$userGroupPaths[$groupId][] = $group->id;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return self::$userGroupPaths[$groupId];
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Method to return the JAccessRules object for an asset.  The returned object can optionally hold
 | |
| 	 * only the rules explicitly set for the asset or the summation of all inherited rules from
 | |
| 	 * parent assets and explicit rules.
 | |
| 	 *
 | |
| 	 * @param   mixed    $asset      Integer asset id or the name of the asset as a string.
 | |
| 	 * @param   boolean  $recursive  True to return the rules object with inherited rules.
 | |
| 	 *
 | |
| 	 * @return  JAccessRules   JAccessRules object for the asset.
 | |
| 	 *
 | |
| 	 * @since   11.1
 | |
| 	 */
 | |
| 	public static function getAssetRules($asset, $recursive = false)
 | |
| 	{
 | |
| 		// Get the database connection object.
 | |
| 		$db = JFactory::getDbo();
 | |
| 
 | |
| 		// Build the database query to get the rules for the asset.
 | |
| 		$query = $db->getQuery(true)
 | |
| 			->select($recursive ? 'b.rules' : 'a.rules')
 | |
| 			->from('#__assets AS a');
 | |
| 
 | |
| 		// SQLsrv change
 | |
| 		$query->group($recursive ? 'b.id, b.rules, b.lft' : 'a.id, a.rules, a.lft');
 | |
| 
 | |
| 		// If the asset identifier is numeric assume it is a primary key, else lookup by name.
 | |
| 		if (is_numeric($asset))
 | |
| 		{
 | |
| 			$query->where('(a.id = ' . (int) $asset . ')');
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			$query->where('(a.name = ' . $db->quote($asset) . ')');
 | |
| 		}
 | |
| 
 | |
| 		// If we want the rules cascading up to the global asset node we need a self-join.
 | |
| 		if ($recursive)
 | |
| 		{
 | |
| 			$query->join('LEFT', '#__assets AS b ON b.lft <= a.lft AND b.rgt >= a.rgt')
 | |
| 				->order('b.lft');
 | |
| 		}
 | |
| 
 | |
| 		// Execute the query and load the rules from the result.
 | |
| 		$db->setQuery($query);
 | |
| 		$result = $db->loadColumn();
 | |
| 
 | |
| 		// Get the root even if the asset is not found and in recursive mode
 | |
| 		if (empty($result))
 | |
| 		{
 | |
| 			$db = JFactory::getDbo();
 | |
| 			$assets = JTable::getInstance('Asset', 'JTable', array('dbo' => $db));
 | |
| 			$rootId = $assets->getRootId();
 | |
| 			$query->clear()
 | |
| 				->select('rules')
 | |
| 				->from('#__assets')
 | |
| 				->where('id = ' . $db->quote($rootId));
 | |
| 			$db->setQuery($query);
 | |
| 			$result = $db->loadResult();
 | |
| 			$result = array($result);
 | |
| 		}
 | |
| 		// Instantiate and return the JAccessRules object for the asset rules.
 | |
| 		$rules = new JAccessRules;
 | |
| 		$rules->mergeCollection($result);
 | |
| 
 | |
| 		return $rules;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Method to return a list of user groups mapped to a user. The returned list can optionally hold
 | |
| 	 * only the groups explicitly mapped to the user or all groups both explicitly mapped and inherited
 | |
| 	 * by the user.
 | |
| 	 *
 | |
| 	 * @param   integer  $userId     Id of the user for which to get the list of groups.
 | |
| 	 * @param   boolean  $recursive  True to include inherited user groups.
 | |
| 	 *
 | |
| 	 * @return  array    List of user group ids to which the user is mapped.
 | |
| 	 *
 | |
| 	 * @since   11.1
 | |
| 	 */
 | |
| 	public static function getGroupsByUser($userId, $recursive = true)
 | |
| 	{
 | |
| 		// Creates a simple unique string for each parameter combination:
 | |
| 		$storeId = $userId . ':' . (int) $recursive;
 | |
| 
 | |
| 		if (!isset(self::$groupsByUser[$storeId]))
 | |
| 		{
 | |
| 			// TODO: Uncouple this from JComponentHelper and allow for a configuration setting or value injection.
 | |
| 			if (class_exists('JComponentHelper'))
 | |
| 			{
 | |
| 				$guestUsergroup = JComponentHelper::getParams('com_users')->get('guest_usergroup', 1);
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				$guestUsergroup = 1;
 | |
| 			}
 | |
| 
 | |
| 			// Guest user (if only the actually assigned group is requested)
 | |
| 			if (empty($userId) && !$recursive)
 | |
| 			{
 | |
| 				$result = array($guestUsergroup);
 | |
| 			}
 | |
| 			// Registered user and guest if all groups are requested
 | |
| 			else
 | |
| 			{
 | |
| 				$db = JFactory::getDbo();
 | |
| 
 | |
| 				// Build the database query to get the rules for the asset.
 | |
| 				$query = $db->getQuery(true)
 | |
| 					->select($recursive ? 'b.id' : 'a.id');
 | |
| 
 | |
| 				if (empty($userId))
 | |
| 				{
 | |
| 					$query->from('#__usergroups AS a')
 | |
| 						->where('a.id = ' . (int) $guestUsergroup);
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					$query->from('#__user_usergroup_map AS map')
 | |
| 						->where('map.user_id = ' . (int) $userId)
 | |
| 						->join('LEFT', '#__usergroups AS a ON a.id = map.group_id');
 | |
| 				}
 | |
| 
 | |
| 				// If we want the rules cascading up to the global asset node we need a self-join.
 | |
| 				if ($recursive)
 | |
| 				{
 | |
| 					$query->join('LEFT', '#__usergroups AS b ON b.lft <= a.lft AND b.rgt >= a.rgt');
 | |
| 				}
 | |
| 
 | |
| 				// Execute the query and load the rules from the result.
 | |
| 				$db->setQuery($query);
 | |
| 				$result = $db->loadColumn();
 | |
| 
 | |
| 				// Clean up any NULL or duplicate values, just in case
 | |
| 				JArrayHelper::toInteger($result);
 | |
| 
 | |
| 				if (empty($result))
 | |
| 				{
 | |
| 					$result = array('1');
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					$result = array_unique($result);
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			self::$groupsByUser[$storeId] = $result;
 | |
| 		}
 | |
| 
 | |
| 		return self::$groupsByUser[$storeId];
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Method to return a list of user Ids contained in a Group
 | |
| 	 *
 | |
| 	 * @param   integer  $groupId    The group Id
 | |
| 	 * @param   boolean  $recursive  Recursively include all child groups (optional)
 | |
| 	 *
 | |
| 	 * @return  array
 | |
| 	 *
 | |
| 	 * @since   11.1
 | |
| 	 * @todo    This method should move somewhere else
 | |
| 	 */
 | |
| 	public static function getUsersByGroup($groupId, $recursive = false)
 | |
| 	{
 | |
| 		// Get a database object.
 | |
| 		$db = JFactory::getDbo();
 | |
| 
 | |
| 		$test = $recursive ? '>=' : '=';
 | |
| 
 | |
| 		// First find the users contained in the group
 | |
| 		$query = $db->getQuery(true)
 | |
| 			->select('DISTINCT(user_id)')
 | |
| 			->from('#__usergroups as ug1')
 | |
| 			->join('INNER', '#__usergroups AS ug2 ON ug2.lft' . $test . 'ug1.lft AND ug1.rgt' . $test . 'ug2.rgt')
 | |
| 			->join('INNER', '#__user_usergroup_map AS m ON ug2.id=m.group_id')
 | |
| 			->where('ug1.id=' . $db->quote($groupId));
 | |
| 
 | |
| 		$db->setQuery($query);
 | |
| 
 | |
| 		$result = $db->loadColumn();
 | |
| 
 | |
| 		// Clean up any NULL values, just in case
 | |
| 		JArrayHelper::toInteger($result);
 | |
| 
 | |
| 		return $result;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Method to return a list of view levels for which the user is authorised.
 | |
| 	 *
 | |
| 	 * @param   integer  $userId  Id of the user for which to get the list of authorised view levels.
 | |
| 	 *
 | |
| 	 * @return  array    List of view levels for which the user is authorised.
 | |
| 	 *
 | |
| 	 * @since   11.1
 | |
| 	 */
 | |
| 	public static function getAuthorisedViewLevels($userId)
 | |
| 	{
 | |
| 		// Get all groups that the user is mapped to recursively.
 | |
| 		$groups = self::getGroupsByUser($userId);
 | |
| 
 | |
| 		// Only load the view levels once.
 | |
| 		if (empty(self::$viewLevels))
 | |
| 		{
 | |
| 			// Get a database object.
 | |
| 			$db = JFactory::getDbo();
 | |
| 
 | |
| 			// Build the base query.
 | |
| 			$query = $db->getQuery(true)
 | |
| 				->select('id, rules')
 | |
| 				->from($db->quoteName('#__viewlevels'));
 | |
| 
 | |
| 			// Set the query for execution.
 | |
| 			$db->setQuery($query);
 | |
| 
 | |
| 			// Build the view levels array.
 | |
| 			foreach ($db->loadAssocList() as $level)
 | |
| 			{
 | |
| 				self::$viewLevels[$level['id']] = (array) json_decode($level['rules']);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// Initialise the authorised array.
 | |
| 		$authorised = array(1);
 | |
| 
 | |
| 		// Find the authorised levels.
 | |
| 		foreach (self::$viewLevels as $level => $rule)
 | |
| 		{
 | |
| 			foreach ($rule as $id)
 | |
| 			{
 | |
| 				if (($id < 0) && (($id * -1) == $userId))
 | |
| 				{
 | |
| 					$authorised[] = $level;
 | |
| 					break;
 | |
| 				}
 | |
| 				// Check to see if the group is mapped to the level.
 | |
| 				elseif (($id >= 0) && in_array($id, $groups))
 | |
| 				{
 | |
| 					$authorised[] = $level;
 | |
| 					break;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return $authorised;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Method to return a list of actions for which permissions can be set given a component and section.
 | |
| 	 *
 | |
| 	 * @param   string  $component  The component from which to retrieve the actions.
 | |
| 	 * @param   string  $section    The name of the section within the component from which to retrieve the actions.
 | |
| 	 *
 | |
| 	 * @return  array  List of actions available for the given component and section.
 | |
| 	 *
 | |
| 	 * @since       11.1
 | |
| 	 * @deprecated  12.3 (Platform) & 4.0 (CMS)  Use JAccess::getActionsFromFile or JAccess::getActionsFromData instead.
 | |
| 	 * @codeCoverageIgnore
 | |
| 	 */
 | |
| 	public static function getActions($component, $section = 'component')
 | |
| 	{
 | |
| 		JLog::add(__METHOD__ . ' is deprecated. Use JAccess::getActionsFromFile or JAccess::getActionsFromData instead.', JLog::WARNING, 'deprecated');
 | |
| 
 | |
| 		$actions = self::getActionsFromFile(
 | |
| 			JPATH_ADMINISTRATOR . '/components/' . $component . '/access.xml',
 | |
| 			"/access/section[@name='" . $section . "']/"
 | |
| 		);
 | |
| 
 | |
| 		if (empty($actions))
 | |
| 		{
 | |
| 			return array();
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			return $actions;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Method to return a list of actions from a file for which permissions can be set.
 | |
| 	 *
 | |
| 	 * @param   string  $file   The path to the XML file.
 | |
| 	 * @param   string  $xpath  An optional xpath to search for the fields.
 | |
| 	 *
 | |
| 	 * @return  boolean|array   False if case of error or the list of actions available.
 | |
| 	 *
 | |
| 	 * @since   12.1
 | |
| 	 */
 | |
| 	public static function getActionsFromFile($file, $xpath = "/access/section[@name='component']/")
 | |
| 	{
 | |
| 		if (!is_file($file) || !is_readable($file))
 | |
| 		{
 | |
| 			// If unable to find the file return false.
 | |
| 			return false;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			// Else return the actions from the xml.
 | |
| 			$xml = simplexml_load_file($file);
 | |
| 
 | |
| 			return self::getActionsFromData($xml, $xpath);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Method to return a list of actions from a string or from an xml for which permissions can be set.
 | |
| 	 *
 | |
| 	 * @param   string|SimpleXMLElement  $data   The XML string or an XML element.
 | |
| 	 * @param   string                   $xpath  An optional xpath to search for the fields.
 | |
| 	 *
 | |
| 	 * @return  boolean|array   False if case of error or the list of actions available.
 | |
| 	 *
 | |
| 	 * @since   12.1
 | |
| 	 */
 | |
| 	public static function getActionsFromData($data, $xpath = "/access/section[@name='component']/")
 | |
| 	{
 | |
| 		// If the data to load isn't already an XML element or string return false.
 | |
| 		if ((!($data instanceof SimpleXMLElement)) && (!is_string($data)))
 | |
| 		{
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		// Attempt to load the XML if a string.
 | |
| 		if (is_string($data))
 | |
| 		{
 | |
| 			try
 | |
| 			{
 | |
| 				$data = new SimpleXMLElement($data);
 | |
| 			}
 | |
| 			catch (Exception $e)
 | |
| 			{
 | |
| 				return false;
 | |
| 			}
 | |
| 
 | |
| 			// Make sure the XML loaded correctly.
 | |
| 			if (!$data)
 | |
| 			{
 | |
| 				return false;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// Initialise the actions array
 | |
| 		$actions = array();
 | |
| 
 | |
| 		// Get the elements from the xpath
 | |
| 		$elements = $data->xpath($xpath . 'action[@name][@title][@description]');
 | |
| 
 | |
| 		// If there some elements, analyse them
 | |
| 		if (!empty($elements))
 | |
| 		{
 | |
| 			foreach ($elements as $action)
 | |
| 			{
 | |
| 				// Add the action to the actions array
 | |
| 				$actions[] = (object) array(
 | |
| 					'name' => (string) $action['name'],
 | |
| 					'title' => (string) $action['title'],
 | |
| 					'description' => (string) $action['description']
 | |
| 				);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// Finally return the actions array
 | |
| 		return $actions;
 | |
| 	}
 | |
| }
 | 
