PageRenderTime 56ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/joomla/access/access.php

https://bitbucket.org/joomla/joomla-platform/
PHP | 376 lines | 183 code | 57 blank | 136 comment | 25 complexity | 363b1a14dc70e76a9535b0315607d79c MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, BSD-3-Clause
  1. <?php
  2. /**
  3. * @package Joomla.Platform
  4. * @subpackage Access
  5. *
  6. * @copyright Copyright (C) 2005 - 2011 Open Source Matters, Inc. All rights reserved.
  7. * @license GNU General Public License version 2 or later; see LICENSE
  8. */
  9. defined('JPATH_PLATFORM') or die;
  10. jimport('joomla.access.rules');
  11. /**
  12. * Class that handles all access authorisation routines.
  13. *
  14. * @package Joomla.Platform
  15. * @subpackage User
  16. * @since 11.1
  17. */
  18. class JAccess
  19. {
  20. /**
  21. * @since 11.1
  22. */
  23. protected static $viewLevels = array();
  24. /**
  25. * @since 11.1
  26. */
  27. protected static $assetRules = array();
  28. /**
  29. * Method to check if a user is authorised to perform an action, optionally on an asset.
  30. *
  31. * @param integer $userId Id of the user for which to check authorisation.
  32. * @param string $action The name of the action to authorise.
  33. * @param mixed $asset Integer asset id or the name of the asset as a string. Defaults to the global asset node.
  34. *
  35. * @return boolean True if authorised.
  36. * @since 11.1
  37. */
  38. public static function check($userId, $action, $asset = null)
  39. {
  40. // Sanitize inputs.
  41. $userId = (int) $userId;
  42. $action = strtolower(preg_replace('#[\s\-]+#', '.', trim($action)));
  43. $asset = strtolower(preg_replace('#[\s\-]+#', '.', trim($asset)));
  44. // Default to the root asset node.
  45. if (empty($asset)) {
  46. $asset = 1;
  47. }
  48. // Get the rules for the asset recursively to root if not already retrieved.
  49. if (empty(self::$assetRules[$asset])) {
  50. self::$assetRules[$asset] = self::getAssetRules($asset, true);
  51. }
  52. // Get all groups against which the user is mapped.
  53. $identities = self::getGroupsByUser($userId);
  54. array_unshift($identities, $userId * -1);
  55. return self::$assetRules[$asset]->allow($action, $identities);
  56. }
  57. /**
  58. * Method to check if a group is authorised to perform an action, optionally on an asset.
  59. *
  60. * @param integer $groupId The path to the group for which to check authorisation.
  61. * @param string $action The name of the action to authorise.
  62. * @param mixed $asset Integer asset id or the name of the asset as a string. Defaults to the global asset node.
  63. *
  64. * @return boolean True if authorised.
  65. * @since 11.1
  66. */
  67. public static function checkGroup($groupId, $action, $asset = null)
  68. {
  69. // Sanitize inputs.
  70. $groupId = (int) $groupId;
  71. $action = strtolower(preg_replace('#[\s\-]+#', '.', trim($action)));
  72. $asset = strtolower(preg_replace('#[\s\-]+#', '.', trim($asset)));
  73. // Get group path for group
  74. $groupPath = self::getGroupPath($groupId);
  75. // Default to the root asset node.
  76. if (empty($asset)) {
  77. $asset = 1;
  78. }
  79. // Get the rules for the asset recursively to root if not already retrieved.
  80. if (empty(self::$assetRules[$asset])) {
  81. self::$assetRules[$asset] = self::getAssetRules($asset, true);
  82. }
  83. return self::$assetRules[$asset]->allow($action, $groupPath);
  84. }
  85. /**
  86. * Gets the parent groups that a leaf group belongs to in its branch back to the root of the tree
  87. * (including the leaf group id).
  88. *
  89. * @param mixed $groupId An integer or array of integers representing the identities to check.
  90. *
  91. * @return mixed True if allowed, false for an explicit deny, null for an implicit deny.
  92. * @since 11.1
  93. */
  94. protected static function getGroupPath($groupId)
  95. {
  96. static $groups, $paths;
  97. // Preload all groups
  98. if (empty($groups)) {
  99. $db = JFactory::getDbo();
  100. $query = $db->getQuery(true)
  101. ->select('parent.id, parent.lft, parent.rgt')
  102. ->from('#__usergroups AS parent')
  103. ->order('parent.lft');
  104. $db->setQuery($query);
  105. $groups = $db->loadObjectList('id');
  106. }
  107. // Make sure groupId is valid
  108. if (!array_key_exists($groupId, $groups))
  109. {
  110. return array();
  111. }
  112. // Get parent groups and leaf group
  113. if (!isset($paths[$groupId])) {
  114. $paths[$groupId] = array();
  115. foreach($groups as $group) {
  116. if ($group->lft <= $groups[$groupId]->lft && $group->rgt >= $groups[$groupId]->rgt) {
  117. $paths[$groupId][] = $group->id;
  118. }
  119. }
  120. }
  121. return $paths[$groupId];
  122. }
  123. /**
  124. * Method to return the JRules object for an asset. The returned object can optionally hold
  125. * only the rules explicitly set for the asset or the summation of all inherited rules from
  126. * parent assets and explicit rules.
  127. *
  128. * @param mixed $asset Integer asset id or the name of the asset as a string.
  129. * @param boolean $recursive True to return the rules object with inherited rules.
  130. *
  131. * @return object JRules object for the asset.
  132. * @since 11.1
  133. */
  134. public static function getAssetRules($asset, $recursive = false)
  135. {
  136. // Get the database connection object.
  137. $db = JFactory::getDbo();
  138. // Build the database query to get the rules for the asset.
  139. $query = $db->getQuery(true);
  140. $query->select($recursive ? 'b.rules' : 'a.rules');
  141. $query->from('#__assets AS a');
  142. $query->group($recursive ? 'b.id' : 'a.id');
  143. // If the asset identifier is numeric assume it is a primary key, else lookup by name.
  144. if (is_numeric($asset)) {
  145. // Get the root even if the asset is not found
  146. $query->where('(a.id = '.(int) $asset.($recursive ? ' OR a.parent_id=0':'').')');
  147. }
  148. else {
  149. // Get the root even if the asset is not found
  150. $query->where('(a.name = '.$db->quote($asset).($recursive ? ' OR a.parent_id=0':'').')');
  151. }
  152. // If we want the rules cascading up to the global asset node we need a self-join.
  153. if ($recursive) {
  154. $query->leftJoin('#__assets AS b ON b.lft <= a.lft AND b.rgt >= a.rgt');
  155. $query->order('b.lft');
  156. }
  157. // Execute the query and load the rules from the result.
  158. $db->setQuery($query);
  159. $result = $db->loadResultArray();
  160. // Instantiate and return the JRules object for the asset rules.
  161. $rules = new JRules;
  162. $rules->mergeCollection($result);
  163. return $rules;
  164. }
  165. /**
  166. * Method to return a list of user groups mapped to a user. The returned list can optionally hold
  167. * only the groups explicitly mapped to the user or all groups both explicitly mapped and inherited
  168. * by the user.
  169. *
  170. * @param integer $userId Id of the user for which to get the list of groups.
  171. * @param boolean $recursive True to include inherited user groups.
  172. *
  173. * @return array List of user group ids to which the user is mapped.
  174. * @since 11.1
  175. */
  176. public static function getGroupsByUser($userId, $recursive = true)
  177. {
  178. static $results = array();
  179. // Creates a simple unique string for each parameter combination:
  180. $storeId = $userId.':'.(int) $recursive;
  181. if (!isset($results[$storeId]))
  182. {
  183. // Guest user
  184. if (empty($userId))
  185. {
  186. $result = array(JComponentHelper::getParams('com_users')->get('guest_usergroup', 1));
  187. }
  188. // Registered user
  189. else
  190. {
  191. $db = JFactory::getDbo();
  192. // Build the database query to get the rules for the asset.
  193. $query = $db->getQuery(true);
  194. $query->select($recursive ? 'b.id' : 'a.id');
  195. $query->from('#__user_usergroup_map AS map');
  196. $query->where('map.user_id = '.(int) $userId);
  197. $query->leftJoin('#__usergroups AS a ON a.id = map.group_id');
  198. // If we want the rules cascading up to the global asset node we need a self-join.
  199. if ($recursive) {
  200. $query->leftJoin('#__usergroups AS b ON b.lft <= a.lft AND b.rgt >= a.rgt');
  201. }
  202. // Execute the query and load the rules from the result.
  203. $db->setQuery($query);
  204. $result = $db->loadResultArray();
  205. // Clean up any NULL or duplicate values, just in case
  206. JArrayHelper::toInteger($result);
  207. if (empty($result)) {
  208. $result = array('1');
  209. }
  210. else {
  211. $result = array_unique($result);
  212. }
  213. }
  214. $results[$storeId] = $result;
  215. }
  216. return $results[$storeId];
  217. }
  218. /**
  219. * Method to return a list of user Ids contained in a Group
  220. *
  221. * @param int $groupId The group Id
  222. * @param boolean $recursive Recursively include all child groups (optional)
  223. *
  224. * @return array
  225. * @since 11.1
  226. * @todo This method should move somewhere else?
  227. */
  228. public static function getUsersByGroup($groupId, $recursive = false)
  229. {
  230. // Get a database object.
  231. $db = JFactory::getDbo();
  232. $test = $recursive ? '>=' : '=';
  233. // First find the users contained in the group
  234. $query = $db->getQuery(true);
  235. $query->select('DISTINCT(user_id)');
  236. $query->from('#__usergroups as ug1');
  237. $query->join('INNER','#__usergroups AS ug2 ON ug2.lft'.$test.'ug1.lft AND ug1.rgt'.$test.'ug2.rgt');
  238. $query->join('INNER','#__user_usergroup_map AS m ON ug2.id=m.group_id');
  239. $query->where('ug1.id='.$db->Quote($groupId));
  240. $db->setQuery($query);
  241. $result = $db->loadResultArray();
  242. // Clean up any NULL values, just in case
  243. JArrayHelper::toInteger($result);
  244. return $result;
  245. }
  246. /**
  247. * Method to return a list of view levels for which the user is authorised.
  248. *
  249. * @param integer $userId Id of the user for which to get the list of authorised view levels.
  250. *
  251. * @return array List of view levels for which the user is authorised.
  252. * @since 11.1
  253. */
  254. public static function getAuthorisedViewLevels($userId)
  255. {
  256. // Get all groups that the user is mapped to recursively.
  257. $groups = self::getGroupsByUser($userId);
  258. // Only load the view levels once.
  259. if (empty(self::$viewLevels)) {
  260. // Get a database object.
  261. $db = JFactory::getDBO();
  262. // Build the base query.
  263. $query = $db->getQuery(true);
  264. $query->select('id, rules');
  265. $query->from('`#__viewlevels`');
  266. // Set the query for execution.
  267. $db->setQuery((string) $query);
  268. // Build the view levels array.
  269. foreach ($db->loadAssocList() as $level) {
  270. self::$viewLevels[$level['id']] = (array) json_decode($level['rules']);
  271. }
  272. }
  273. // Initialise the authorised array.
  274. $authorised = array(1);
  275. // Find the authorised levels.
  276. foreach (self::$viewLevels as $level => $rule)
  277. {
  278. foreach ($rule as $id)
  279. {
  280. if (($id < 0) && (($id * -1) == $userId)) {
  281. $authorised[] = $level;
  282. break;
  283. }
  284. // Check to see if the group is mapped to the level.
  285. elseif (($id >= 0) && in_array($id, $groups)) {
  286. $authorised[] = $level;
  287. break;
  288. }
  289. }
  290. }
  291. return $authorised;
  292. }
  293. /**
  294. * Method to return a list of actions for which permissions can be set given a component and section.
  295. *
  296. * @param string $component The component from which to retrieve the actions.
  297. * @param string $section The name of the section within the component from which to retrieve the actions.
  298. *
  299. * @return array List of actions available for the given component and section.
  300. * @since 11.1
  301. */
  302. public static function getActions($component, $section = 'component')
  303. {
  304. $actions = array();
  305. if (is_file(JPATH_ADMINISTRATOR.'/components/'.$component.'/access.xml')) {
  306. $xml = simplexml_load_file(JPATH_ADMINISTRATOR.'/components/'.$component.'/access.xml');
  307. foreach ($xml->children() as $child)
  308. {
  309. if ($section == (string) $child['name']) {
  310. foreach ($child->children() as $action) {
  311. $actions[] = (object) array('name' => (string) $action['name'], 'title' => (string) $action['title'], 'description' => (string) $action['description']);
  312. }
  313. break;
  314. }
  315. }
  316. }
  317. return $actions;
  318. }
  319. }