PageRenderTime 25ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/joomla/access/access.php

https://bitbucket.org/pastor399/newcastleunifc
PHP | 568 lines | 291 code | 66 blank | 211 comment | 32 complexity | 66946af7c9afcc47f032cdb308dde587 MD5 | raw file
  1. <?php
  2. /**
  3. * @package Joomla.Platform
  4. * @subpackage Access
  5. *
  6. * @copyright Copyright (C) 2005 - 2013 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.utilities.arrayhelper');
  11. /**
  12. * Class that handles all access authorisation routines.
  13. *
  14. * @package Joomla.Platform
  15. * @subpackage Access
  16. * @since 11.1
  17. */
  18. class JAccess
  19. {
  20. /**
  21. * Array of view levels
  22. *
  23. * @var array
  24. * @since 11.1
  25. */
  26. protected static $viewLevels = array();
  27. /**
  28. * Array of rules for the asset
  29. *
  30. * @var array
  31. * @since 11.1
  32. */
  33. protected static $assetRules = array();
  34. /**
  35. * Array of user groups.
  36. *
  37. * @var array
  38. * @since 11.1
  39. */
  40. protected static $userGroups = array();
  41. /**
  42. * Array of user group paths.
  43. *
  44. * @var array
  45. * @since 11.1
  46. */
  47. protected static $userGroupPaths = array();
  48. /**
  49. * Array of cached groups by user.
  50. *
  51. * @var array
  52. * @since 11.1
  53. */
  54. protected static $groupsByUser = array();
  55. /**
  56. * Method for clearing static caches.
  57. *
  58. * @return void
  59. *
  60. * @since 11.3
  61. */
  62. public static function clearStatics()
  63. {
  64. self::$viewLevels = array();
  65. self::$assetRules = array();
  66. self::$userGroups = array();
  67. self::$userGroupPaths = array();
  68. self::$groupsByUser = array();
  69. }
  70. /**
  71. * Method to check if a user is authorised to perform an action, optionally on an asset.
  72. *
  73. * @param integer $userId Id of the user for which to check authorisation.
  74. * @param string $action The name of the action to authorise.
  75. * @param mixed $asset Integer asset id or the name of the asset as a string. Defaults to the global asset node.
  76. *
  77. * @return boolean True if authorised.
  78. *
  79. * @since 11.1
  80. */
  81. public static function check($userId, $action, $asset = null)
  82. {
  83. // Sanitise inputs.
  84. $userId = (int) $userId;
  85. $action = strtolower(preg_replace('#[\s\-]+#', '.', trim($action)));
  86. $asset = strtolower(preg_replace('#[\s\-]+#', '.', trim($asset)));
  87. // Default to the root asset node.
  88. if (empty($asset))
  89. {
  90. $db = JFactory::getDbo();
  91. $assets = JTable::getInstance('Asset', 'JTable', array('dbo' => $db));
  92. $rootId = $assets->getRootId();
  93. $asset = $rootId;
  94. }
  95. // Get the rules for the asset recursively to root if not already retrieved.
  96. if (empty(self::$assetRules[$asset]))
  97. {
  98. self::$assetRules[$asset] = self::getAssetRules($asset, true);
  99. }
  100. // Get all groups against which the user is mapped.
  101. $identities = self::getGroupsByUser($userId);
  102. array_unshift($identities, $userId * -1);
  103. return self::$assetRules[$asset]->allow($action, $identities);
  104. }
  105. /**
  106. * Method to check if a group is authorised to perform an action, optionally on an asset.
  107. *
  108. * @param integer $groupId The path to the group for which to check authorisation.
  109. * @param string $action The name of the action to authorise.
  110. * @param mixed $asset Integer asset id or the name of the asset as a string. Defaults to the global asset node.
  111. *
  112. * @return boolean True if authorised.
  113. *
  114. * @since 11.1
  115. */
  116. public static function checkGroup($groupId, $action, $asset = null)
  117. {
  118. // Sanitize inputs.
  119. $groupId = (int) $groupId;
  120. $action = strtolower(preg_replace('#[\s\-]+#', '.', trim($action)));
  121. $asset = strtolower(preg_replace('#[\s\-]+#', '.', trim($asset)));
  122. // Get group path for group
  123. $groupPath = self::getGroupPath($groupId);
  124. // Default to the root asset node.
  125. if (empty($asset))
  126. {
  127. $db = JFactory::getDbo();
  128. $assets = JTable::getInstance('Asset', 'JTable', array('dbo' => $db));
  129. $rootId = $assets->getRootId();
  130. }
  131. // Get the rules for the asset recursively to root if not already retrieved.
  132. if (empty(self::$assetRules[$asset]))
  133. {
  134. self::$assetRules[$asset] = self::getAssetRules($asset, true);
  135. }
  136. return self::$assetRules[$asset]->allow($action, $groupPath);
  137. }
  138. /**
  139. * Gets the parent groups that a leaf group belongs to in its branch back to the root of the tree
  140. * (including the leaf group id).
  141. *
  142. * @param mixed $groupId An integer or array of integers representing the identities to check.
  143. *
  144. * @return mixed True if allowed, false for an explicit deny, null for an implicit deny.
  145. *
  146. * @since 11.1
  147. */
  148. protected static function getGroupPath($groupId)
  149. {
  150. // Preload all groups
  151. if (empty(self::$userGroups))
  152. {
  153. $db = JFactory::getDbo();
  154. $query = $db->getQuery(true)
  155. ->select('parent.id, parent.lft, parent.rgt')
  156. ->from('#__usergroups AS parent')
  157. ->order('parent.lft');
  158. $db->setQuery($query);
  159. self::$userGroups = $db->loadObjectList('id');
  160. }
  161. // Make sure groupId is valid
  162. if (!array_key_exists($groupId, self::$userGroups))
  163. {
  164. return array();
  165. }
  166. // Get parent groups and leaf group
  167. if (!isset(self::$userGroupPaths[$groupId]))
  168. {
  169. self::$userGroupPaths[$groupId] = array();
  170. foreach (self::$userGroups as $group)
  171. {
  172. if ($group->lft <= self::$userGroups[$groupId]->lft && $group->rgt >= self::$userGroups[$groupId]->rgt)
  173. {
  174. self::$userGroupPaths[$groupId][] = $group->id;
  175. }
  176. }
  177. }
  178. return self::$userGroupPaths[$groupId];
  179. }
  180. /**
  181. * Method to return the JAccessRules object for an asset. The returned object can optionally hold
  182. * only the rules explicitly set for the asset or the summation of all inherited rules from
  183. * parent assets and explicit rules.
  184. *
  185. * @param mixed $asset Integer asset id or the name of the asset as a string.
  186. * @param boolean $recursive True to return the rules object with inherited rules.
  187. *
  188. * @return JAccessRules JAccessRules object for the asset.
  189. *
  190. * @since 11.1
  191. */
  192. public static function getAssetRules($asset, $recursive = false)
  193. {
  194. // Get the database connection object.
  195. $db = JFactory::getDbo();
  196. // Build the database query to get the rules for the asset.
  197. $query = $db->getQuery(true)
  198. ->select($recursive ? 'b.rules' : 'a.rules')
  199. ->from('#__assets AS a');
  200. // SQLsrv change
  201. $query->group($recursive ? 'b.id, b.rules, b.lft' : 'a.id, a.rules, a.lft');
  202. // If the asset identifier is numeric assume it is a primary key, else lookup by name.
  203. if (is_numeric($asset))
  204. {
  205. $query->where('(a.id = ' . (int) $asset . ')');
  206. }
  207. else
  208. {
  209. $query->where('(a.name = ' . $db->quote($asset) . ')');
  210. }
  211. // If we want the rules cascading up to the global asset node we need a self-join.
  212. if ($recursive)
  213. {
  214. $query->join('LEFT', '#__assets AS b ON b.lft <= a.lft AND b.rgt >= a.rgt')
  215. ->order('b.lft');
  216. }
  217. // Execute the query and load the rules from the result.
  218. $db->setQuery($query);
  219. $result = $db->loadColumn();
  220. // Get the root even if the asset is not found and in recursive mode
  221. if (empty($result))
  222. {
  223. $db = JFactory::getDbo();
  224. $assets = JTable::getInstance('Asset', 'JTable', array('dbo' => $db));
  225. $rootId = $assets->getRootId();
  226. $query = $db->getQuery(true)
  227. ->select('rules')
  228. ->from('#__assets')
  229. ->where('id = ' . $db->quote($rootId));
  230. $db->setQuery($query);
  231. $result = $db->loadResult();
  232. $result = array($result);
  233. }
  234. // Instantiate and return the JAccessRules object for the asset rules.
  235. $rules = new JAccessRules;
  236. $rules->mergeCollection($result);
  237. return $rules;
  238. }
  239. /**
  240. * Method to return a list of user groups mapped to a user. The returned list can optionally hold
  241. * only the groups explicitly mapped to the user or all groups both explicitly mapped and inherited
  242. * by the user.
  243. *
  244. * @param integer $userId Id of the user for which to get the list of groups.
  245. * @param boolean $recursive True to include inherited user groups.
  246. *
  247. * @return array List of user group ids to which the user is mapped.
  248. *
  249. * @since 11.1
  250. */
  251. public static function getGroupsByUser($userId, $recursive = true)
  252. {
  253. // Creates a simple unique string for each parameter combination:
  254. $storeId = $userId . ':' . (int) $recursive;
  255. if (!isset(self::$groupsByUser[$storeId]))
  256. {
  257. // TODO: Uncouple this from JComponentHelper and allow for a configuration setting or value injection.
  258. if (class_exists('JComponentHelper'))
  259. {
  260. $guestUsergroup = JComponentHelper::getParams('com_users')->get('guest_usergroup', 1);
  261. }
  262. else
  263. {
  264. $guestUsergroup = 1;
  265. }
  266. // Guest user (if only the actually assigned group is requested)
  267. if (empty($userId) && !$recursive)
  268. {
  269. $result = array($guestUsergroup);
  270. }
  271. // Registered user and guest if all groups are requested
  272. else
  273. {
  274. $db = JFactory::getDbo();
  275. // Build the database query to get the rules for the asset.
  276. $query = $db->getQuery(true)
  277. ->select($recursive ? 'b.id' : 'a.id');
  278. if (empty($userId))
  279. {
  280. $query->from('#__usergroups AS a')
  281. ->where('a.id = ' . (int) $guestUsergroup);
  282. }
  283. else
  284. {
  285. $query->from('#__user_usergroup_map AS map')
  286. ->where('map.user_id = ' . (int) $userId)
  287. ->join('LEFT', '#__usergroups AS a ON a.id = map.group_id');
  288. }
  289. // If we want the rules cascading up to the global asset node we need a self-join.
  290. if ($recursive)
  291. {
  292. $query->join('LEFT', '#__usergroups AS b ON b.lft <= a.lft AND b.rgt >= a.rgt');
  293. }
  294. // Execute the query and load the rules from the result.
  295. $db->setQuery($query);
  296. $result = $db->loadColumn();
  297. // Clean up any NULL or duplicate values, just in case
  298. JArrayHelper::toInteger($result);
  299. if (empty($result))
  300. {
  301. $result = array('1');
  302. }
  303. else
  304. {
  305. $result = array_unique($result);
  306. }
  307. }
  308. self::$groupsByUser[$storeId] = $result;
  309. }
  310. return self::$groupsByUser[$storeId];
  311. }
  312. /**
  313. * Method to return a list of user Ids contained in a Group
  314. *
  315. * @param integer $groupId The group Id
  316. * @param boolean $recursive Recursively include all child groups (optional)
  317. *
  318. * @return array
  319. *
  320. * @since 11.1
  321. * @todo This method should move somewhere else
  322. */
  323. public static function getUsersByGroup($groupId, $recursive = false)
  324. {
  325. // Get a database object.
  326. $db = JFactory::getDbo();
  327. $test = $recursive ? '>=' : '=';
  328. // First find the users contained in the group
  329. $query = $db->getQuery(true)
  330. ->select('DISTINCT(user_id)')
  331. ->from('#__usergroups as ug1')
  332. ->join('INNER', '#__usergroups AS ug2 ON ug2.lft' . $test . 'ug1.lft AND ug1.rgt' . $test . 'ug2.rgt')
  333. ->join('INNER', '#__user_usergroup_map AS m ON ug2.id=m.group_id')
  334. ->where('ug1.id=' . $db->quote($groupId));
  335. $db->setQuery($query);
  336. $result = $db->loadColumn();
  337. // Clean up any NULL values, just in case
  338. JArrayHelper::toInteger($result);
  339. return $result;
  340. }
  341. /**
  342. * Method to return a list of view levels for which the user is authorised.
  343. *
  344. * @param integer $userId Id of the user for which to get the list of authorised view levels.
  345. *
  346. * @return array List of view levels for which the user is authorised.
  347. *
  348. * @since 11.1
  349. */
  350. public static function getAuthorisedViewLevels($userId)
  351. {
  352. // Get all groups that the user is mapped to recursively.
  353. $groups = self::getGroupsByUser($userId);
  354. // Only load the view levels once.
  355. if (empty(self::$viewLevels))
  356. {
  357. // Get a database object.
  358. $db = JFactory::getDbo();
  359. // Build the base query.
  360. $query = $db->getQuery(true)
  361. ->select('id, rules')
  362. ->from($db->quoteName('#__viewlevels'));
  363. // Set the query for execution.
  364. $db->setQuery($query);
  365. // Build the view levels array.
  366. foreach ($db->loadAssocList() as $level)
  367. {
  368. self::$viewLevels[$level['id']] = (array) json_decode($level['rules']);
  369. }
  370. }
  371. // Initialise the authorised array.
  372. $authorised = array(1);
  373. // Find the authorised levels.
  374. foreach (self::$viewLevels as $level => $rule)
  375. {
  376. foreach ($rule as $id)
  377. {
  378. if (($id < 0) && (($id * -1) == $userId))
  379. {
  380. $authorised[] = $level;
  381. break;
  382. }
  383. // Check to see if the group is mapped to the level.
  384. elseif (($id >= 0) && in_array($id, $groups))
  385. {
  386. $authorised[] = $level;
  387. break;
  388. }
  389. }
  390. }
  391. return $authorised;
  392. }
  393. /**
  394. * Method to return a list of actions for which permissions can be set given a component and section.
  395. *
  396. * @param string $component The component from which to retrieve the actions.
  397. * @param string $section The name of the section within the component from which to retrieve the actions.
  398. *
  399. * @return array List of actions available for the given component and section.
  400. *
  401. * @since 11.1
  402. *
  403. * @deprecated 12.3 Use JAccess::getActionsFromFile or JAccess::getActionsFromData instead.
  404. *
  405. * @codeCoverageIgnore
  406. *
  407. */
  408. public static function getActions($component, $section = 'component')
  409. {
  410. JLog::add(__METHOD__ . ' is deprecated. Use JAccess::getActionsFromFile or JAcces::getActionsFromData instead.', JLog::WARNING, 'deprecated');
  411. $actions = self::getActionsFromFile(
  412. JPATH_ADMINISTRATOR . '/components/' . $component . '/access.xml',
  413. "/access/section[@name='" . $section . "']/"
  414. );
  415. if (empty($actions))
  416. {
  417. return array();
  418. }
  419. else
  420. {
  421. return $actions;
  422. }
  423. }
  424. /**
  425. * Method to return a list of actions from a file for which permissions can be set.
  426. *
  427. * @param string $file The path to the XML file.
  428. * @param string $xpath An optional xpath to search for the fields.
  429. *
  430. * @return boolean|array False if case of error or the list of actions available.
  431. *
  432. * @since 12.1
  433. */
  434. public static function getActionsFromFile($file, $xpath = "/access/section[@name='component']/")
  435. {
  436. if (!is_file($file) || !is_readable($file))
  437. {
  438. // If unable to find the file return false.
  439. return false;
  440. }
  441. else
  442. {
  443. // Else return the actions from the xml.
  444. $xml = simplexml_load_file($file);
  445. return self::getActionsFromData($xml, $xpath);
  446. }
  447. }
  448. /**
  449. * Method to return a list of actions from a string or from an xml for which permissions can be set.
  450. *
  451. * @param string|SimpleXMLElement $data The XML string or an XML element.
  452. * @param string $xpath An optional xpath to search for the fields.
  453. *
  454. * @return boolean|array False if case of error or the list of actions available.
  455. *
  456. * @since 12.1
  457. */
  458. public static function getActionsFromData($data, $xpath = "/access/section[@name='component']/")
  459. {
  460. // If the data to load isn't already an XML element or string return false.
  461. if ((!($data instanceof SimpleXMLElement)) && (!is_string($data)))
  462. {
  463. return false;
  464. }
  465. // Attempt to load the XML if a string.
  466. if (is_string($data))
  467. {
  468. try
  469. {
  470. $data = new SimpleXMLElement($data);
  471. }
  472. catch (Exception $e)
  473. {
  474. return false;
  475. }
  476. // Make sure the XML loaded correctly.
  477. if (!$data)
  478. {
  479. return false;
  480. }
  481. }
  482. // Initialise the actions array
  483. $actions = array();
  484. // Get the elements from the xpath
  485. $elements = $data->xpath($xpath . 'action[@name][@title][@description]');
  486. // If there some elements, analyse them
  487. if (!empty($elements))
  488. {
  489. foreach ($elements as $action)
  490. {
  491. // Add the action to the actions array
  492. $actions[] = (object) array(
  493. 'name' => (string) $action['name'],
  494. 'title' => (string) $action['title'],
  495. 'description' => (string) $action['description']
  496. );
  497. }
  498. }
  499. // Finally return the actions array
  500. return $actions;
  501. }
  502. }