PageRenderTime 42ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/contentmanager/code/trunk/administrator/components/com_contentmanager/libraries/jxtended/acl/acl.php

https://bitbucket.org/eddieajau/the-art-of-joomla-archive
PHP | 470 lines | 256 code | 64 blank | 150 comment | 65 complexity | 128a63d5e9d2e21bb67c96e235733d53 MD5 | raw file
  1. <?php
  2. /**
  3. * @version $Id: acl.php 160 2009-07-09 00:06:09Z eddieajau $
  4. * @copyright Copyright (C) 2008 JXtended, LLC. All rights reserved.
  5. * @license GNU General Public License
  6. *
  7. * This and supporting files based heavily on phpGacl
  8. *
  9. * @copyright Copyright (C) 2002,2003 Mike Benoit
  10. * @link http://phpgacl.sourceforge.net/
  11. * @license GNU Lesser General Public
  12. */
  13. defined('JPATH_BASE') or die;
  14. jximport2('jxtended.database.query');
  15. JTable::addIncludePath(dirname(dirname(__FILE__)).DS.'database'.DS.'table');
  16. /**
  17. * @package JXtended.Libraries
  18. * @subpackage Acl
  19. * @version 1.0
  20. * @static
  21. */
  22. class JxAcl
  23. {
  24. /**
  25. * Authorises either the current or supplied user to perform an action
  26. *
  27. * @param string $actionSection The section of the action of test
  28. * @param string $action The action value to test
  29. * @param string $assetSection An optional asset section to test
  30. * @param int $asset An optional asset id to test (both asset section and id must be used together)
  31. * @param int $userId An optional user id to test against, otherwise the current user is used.
  32. *
  33. * @return boolean
  34. */
  35. function authorise($actionSection, $action, $assetSection = null, $asset = null, $userId = null)
  36. {
  37. // Get the current user if not supplied
  38. if ($userId === null) {
  39. $userId = JFactory::getUser()->get('id');
  40. }
  41. // Check for the root user
  42. $config = new JConfig;
  43. if ($userId == $config->root_user) {
  44. return true;
  45. }
  46. $result = JAcl::check($actionSection, $action, 'users', $userId, $assetSection, $asset);
  47. return $result['allow'];
  48. }
  49. /**
  50. * The Main function that does the actual ACL lookup.
  51. *
  52. * @param string The ACO section value
  53. * @param string The ACO value
  54. * @param string The ARO section value
  55. * @param string The ARO section
  56. * @param string The AXO section value (optional)
  57. * @param string The AXO section value (optional)
  58. * @param string The value of the root ARO group (optional)
  59. * @param string The value of the root AXO group (optional)
  60. *
  61. * @return array Returns as much information as possible about the ACL so other functions can trim it down and omit unwanted data.
  62. */
  63. function check($acoSectionValue, $acoValue, $aroSectionValue, $aroValue, $axoSectionValue=NULL, $axoValue=NULL, $rootAroGroup=NULL, $rootAxoGroup=NULL)
  64. {
  65. // @todo More advanced caching to span session
  66. static $cache;
  67. // Simple cache
  68. if ($cache == null) {
  69. $cache = array();
  70. }
  71. $cacheId = 'acl_query_'.$acoSectionValue.'-'.$acoValue.'-'.$aroSectionValue.'-'.$aroValue.'-'.$axoSectionValue.'-'.$axoValue.'-'.$rootAroGroup.'-'.$rootAxoGroup;
  72. if (!isset($cache[$cacheId]))
  73. {
  74. /*
  75. * This query is where all the magic happens.
  76. * The ordering is very important here, as well very tricky to get correct.
  77. * Currently there can be duplicate ACLs, or ones that step on each other toes. In this case, the ACL that was last updated/created
  78. * is used.
  79. *
  80. * This is probably where the most optimizations can be made.
  81. */
  82. $sql_aro_group_ids = null;
  83. $sql_axo_group_ids = null;
  84. $db = &JFactory::getDbo();
  85. $order_by = array();
  86. $query = new JxQuery;
  87. $query->select('a.id,a.allow,a.return_value');
  88. $query->from('#__core_acl_acl AS a');
  89. $query->join('LEFT', '#__core_acl_aco_map ac ON ac.acl_id=a.id');
  90. $query->join('LEFT', '#__core_acl_aro_map ar ON ar.acl_id=a.id');
  91. $query->join('LEFT', '#__core_acl_axo_map ax ON ax.acl_id=a.id');
  92. // Get all groups mapped to this ARO/AXO
  93. $aroGroupIds = JxAcl::acl_get_groups($aroSectionValue, $aroValue, $rootAroGroup, 'ARO');
  94. if (is_array($aroGroupIds) AND !empty($aroGroupIds))
  95. {
  96. $sql_aro_group_ids = implode(',', $aroGroupIds);
  97. $query->join('LEFT', '#__core_acl_aro_groups_map arg ON arg.acl_id=a.id');
  98. $query->join('LEFT', '#__core_acl_aro_groups rg ON rg.id=arg.group_id');
  99. }
  100. if (($axoSectionValue !== '' || $axoSectionValue !== null) AND ($axoValue !== '' || $axoValue !== null))
  101. {
  102. $axo_group_ids = JxAcl::acl_get_groups($axoSectionValue, $axoValue, $rootAxoGroup, 'AXO');
  103. if (is_array($axo_group_ids) AND !empty($axo_group_ids))
  104. {
  105. $sql_axo_group_ids = implode(',', $axo_group_ids);
  106. }
  107. }
  108. // this join is necessary to weed out rules associated with axo groups
  109. $query->join('LEFT', '#__core_acl_axo_groups_map axg ON axg.acl_id=a.id');
  110. if ($sql_axo_group_ids) {
  111. $query->join('LEFT', '#__core_acl_axo_groups xg ON xg.id=axg.group_id');
  112. }
  113. $query->where('a.enabled = 1');
  114. $query->where('ac.section_value='. $db->quote($acoSectionValue));
  115. $query->where('ac.value='. $db->quote($acoValue));
  116. $temp = '(ar.section_value='. $db->quote($aroSectionValue) .' AND ar.value='. $db->quote($aroValue) .')';
  117. if ($sql_aro_group_ids) {
  118. $temp .= ' OR rg.id IN ('. $sql_aro_group_ids .')';
  119. }
  120. $query->where('('.$temp.')');
  121. if ($axoSectionValue == '' AND $axoValue === null)
  122. {
  123. $temp = '(ax.section_value IS NULL AND ax.value IS NULL)';
  124. $query->order('(CASE WHEN ar.value IS NULL THEN 0 ELSE 1 END) DESC');
  125. $query->order('(rg.rgt-rg.lft) ASC');
  126. }
  127. else {
  128. $temp = '(ax.section_value='. $db->quote($axoSectionValue) .' AND ax.value='. $db->quote($axoValue) .')';
  129. }
  130. if ($sql_axo_group_ids) {
  131. $temp .= ' OR xg.id IN ('. $sql_axo_group_ids .')';
  132. $query->order('(CASE WHEN ax.value IS NULL THEN 0 ELSE 1 END) DESC');
  133. $query->order('(xg.rgt-xg.lft) ASC');
  134. }
  135. else {
  136. $temp .= ' AND axg.group_id IS NULL';
  137. }
  138. $query->where('('.$temp.')');
  139. /*
  140. * The ordering is always very tricky and makes all the difference in the world.
  141. * Order (ar.value IS NOT NULL) DESC should put ACLs given to specific AROs
  142. * ahead of any ACLs given to groups. This works well for exceptions to groups.
  143. */
  144. $query->order('a.updated_date DESC');
  145. // we are only interested in the first row
  146. $db->setQuery($query->toString(), 0, 1);
  147. //echo $db->getQuery().'<hr />';
  148. $row = $db->loadRow();
  149. /*
  150. * Return ACL ID. This is the key to "hooking" extras like pricing assigned to ACLs etc... Very useful.
  151. */
  152. if (is_array($row)) {
  153. // Permission granted?
  154. // This below oneliner is very confusing.
  155. //$allow = (isset($row[1]) AND $row[1] == 1);
  156. //Prefer this.
  157. if (isset($row[1]) AND $row[1] == 1) {
  158. $allow = TRUE;
  159. } else {
  160. $allow = FALSE;
  161. }
  162. $cache[$cacheId] = array('acl_id' => $row[0], 'return_value' => $row[2], 'allow' => $allow);
  163. }
  164. else {
  165. // Permission denied.
  166. $cache[$cacheId] = array('acl_id' => NULL, 'return_value' => NULL, 'allow' => FALSE);
  167. }
  168. }
  169. return $cache[$cacheId];
  170. }
  171. /**
  172. * Grabs all groups mapped to an ARO.
  173. *
  174. * A root group value can be specified for looking at sub-tree
  175. * (results include the root group)
  176. *
  177. * @param string The section value or the ARO or AXO
  178. * @param string The value of the ARO or AXO
  179. * @param integer The value of the group to start at (optional)
  180. * @param string The type of group, either ARO or AXO (optional)
  181. */
  182. function acl_get_groups($sectionValue, $value, $rootGroupValue=NULL, $type='ARO')
  183. {
  184. // @todo More advanced caching to span session
  185. static $cache;
  186. $db = &JFactory::getDbo();
  187. $type = strtolower($type);
  188. if ($type != 'aro' && $type != 'axo') {
  189. // @todo Throw an expection
  190. return array();
  191. }
  192. if (($sectionValue === '' || $sectionValue === null) && ($value === '' || $value === null)) {
  193. return array();
  194. }
  195. // Simple cache
  196. if ($cache == null) {
  197. $cache = array();
  198. }
  199. // Generate unique cache id.
  200. $cacheId = 'acl_get_groups_'.$sectionValue.'-'.$value.'-'.$rootGroupValue.'-'.$type;
  201. if (!isset($cache[$cacheId]))
  202. {
  203. $query = new JxQuery;
  204. // Make sure we get the groups
  205. $query->select('DISTINCT g2.id');
  206. $query->from('#__core_acl_'.$type.' AS o');
  207. $query->join('INNER', '#__core_acl_groups_'.$type.'_map AS gm ON gm.'. $type .'_id=o.id');
  208. $query->join('INNER', '#__core_acl_'.$type.'_groups AS g1 ON g1.id = gm.group_id');
  209. $query->where('(o.section_value='. $db->quote($sectionValue) .' AND o.value='. $db->quote($value) .')');
  210. /*
  211. * If root group value is specified, we have to narrow this query down
  212. * to just groups deeper in the tree then what is specified.
  213. * This essentially creates a virtual "subtree" and ignores all outside groups.
  214. * Useful for sites like sourceforge where you may seperate groups by "project".
  215. */
  216. if ($rootGroupValue != '') {
  217. $query->join('INNER', '#__core_acl_'.$type.'_groups AS g3 ON g3.value='. $db->quote($rootGroupValue));
  218. $query->join('INNER', '#__core_acl_'.$type.'_groups AS g2 ON ((g2.lft BETWEEN g3.lft AND g1.lft) AND (g2.rgt BETWEEN g1.rgt AND g3.rgt))');
  219. }
  220. else {
  221. $query->join('INNER', '#__core_acl_'.$type.'_groups AS g2 ON (g2.lft <= g1.lft AND g2.rgt >= g1.rgt)');
  222. }
  223. $db->setQuery($query->toString());
  224. //echo $db->getQuery();
  225. $cache[$cacheId] = $db->loadResultArray();
  226. }
  227. return $cache[$cacheId];
  228. }
  229. /**
  230. * Get a list of the allows Asset Groups that a user is allowed for a given Action
  231. *
  232. * This helper method is typicall used in conjuction with Type 3 Rules where an action is applied
  233. * to one or more asset groups. An primary object will typically be given a single asset level
  234. * in an `access` field. The usage could be something like:
  235. *
  236. * $assetGroups = JxAcl::getAllowedAssetGroups('core', 'global.view');
  237. *
  238. * $query = new JQuery;
  239. * $query->select('a.*');
  240. * $query->from('#__content AS a');
  241. * $query->where('a.published = 1');
  242. * $query->where('a.access IN ('.$assetGroups.')');
  243. * $db = &JFactory:getDBO();
  244. * $db->setQuery();
  245. * $items = $db->loadObjectList();
  246. *
  247. * @param string $actionSection The section value for the action
  248. * @param mixed $action A single action value, or an array of action values, to be tested
  249. * @param integer $userId An optional user Id. The current use it used if not supplied
  250. * @param boolean $asString Return the results as a string, otherwise as an array
  251. *
  252. * @return string An array or comma separated list of Asset Groups that the user is authorised to perform the action on
  253. */
  254. function getAllowedAssetGroups($actionSection, $action, $userId = null, $asString = true)
  255. {
  256. static $cache;
  257. // @todo This result is ideal for caching in the session as it need only be calculated once for the user for each context
  258. if (empty($actionSection)) {
  259. return '0';
  260. }
  261. if (empty($assetSection)) {
  262. $assetSection = $actionSection;
  263. }
  264. if ($userId === null) {
  265. $user = &JFactory::getUser();
  266. $userId = $user->get('id');
  267. }
  268. // Generate unique cache id.
  269. $cacheId = $actionSection.'-'.$action.'-'.$userId.'-'.$asString;
  270. // Simple cache
  271. if ($cache == null) {
  272. $cache = array();
  273. }
  274. if (!isset($cache[$cacheId]))
  275. {
  276. $db = &JFactory::getDBO();
  277. $query = new JXQuery;
  278. $query->select('GROUP_CONCAT(DISTINCT axog.value SEPARATOR \',\')');
  279. $query->from('jos_core_acl_aco_map AS am');
  280. $query->join('INNER', '#__core_acl_acl AS acl ON acl.id = am.acl_id');
  281. $query->join('INNER', '#__core_acl_aro_groups_map AS agm ON agm.acl_id = am.acl_id');
  282. $query->join('LEFT', '#__core_acl_axo_groups_map AS axogm ON axogm.acl_id = am.acl_id');
  283. $query->join('INNER', '#__core_acl_axo_groups AS axog ON axog.id = axogm.group_id');
  284. $query->join('INNER', '#__core_acl_groups_aro_map AS garom ON garom.group_id = agm.group_id');
  285. $query->join('INNER', '#__core_acl_aro AS aro ON aro.id = garom.aro_id');
  286. $query->where('am.section_value = '.$db->Quote($actionSection));
  287. if (is_array($action))
  288. {
  289. $action = array_map(array($db, 'Quote'), $action);
  290. $query->where('am.value IN ('.implode(',', $action).')');
  291. }
  292. else {
  293. $query->where('am.value = '.$db->Quote($action));
  294. }
  295. $query->where('acl.enabled = 1');
  296. $query->where('acl.allow = 1');
  297. $query->where('aro.value = '.(int) $userId);
  298. $db->setQuery($query->toString());
  299. if ($ids = $db->loadResult()) {
  300. if ($asString) {
  301. $cache[$cacheId] = $ids;
  302. }
  303. else {
  304. $cache[$cacheId] = explode(',', $ids);
  305. }
  306. }
  307. else {
  308. if ($asString) {
  309. $cache[$cacheId] = '0';
  310. }
  311. else {
  312. $cache[$cacheId] = array(0);
  313. }
  314. }
  315. }
  316. return $cache[$cacheId];
  317. }
  318. /**
  319. * Get a list of the Assets on which a user can perform the given Action
  320. *
  321. * This helper method is typicall used in conjuction with Type 3 Rules where an action is applied
  322. * to one or more asset groups. An primary object will typically be given a single asset level
  323. * in an `access` field. The usage could be something like:
  324. *
  325. * $assets = JxAcl::getAllowedAssets('com_content', 'create.article');
  326. *
  327. * $query = new JQuery;
  328. * $query->select('a.*');
  329. * $query->from('#__content AS a');
  330. * $query->where('a.published = 1');
  331. * $query->where('a.id IN ('.$assets.')');
  332. * $db = &JFactory:getDBO();
  333. * $db->setQuery();
  334. * $items = $db->loadObjectList();
  335. *
  336. * @param string $actionSection The section value for the action
  337. * @param mixed $action A single action value, or an array of action values, to be tested
  338. * @param integer $userId An optional user Id. The current use it used if not supplied
  339. * @param boolean $asString Return the results as a string, otherwise as an array
  340. *
  341. * @return string An array or comma separated list of Asset Groups that the user is authorised to perform the action on
  342. */
  343. function getAllowedAssets($actionSection, $action, $userId = null, $asString = true)
  344. {
  345. static $cache;
  346. // @todo This result is ideal for caching in the session as it need only be calculated once for the user for each context
  347. if (empty($actionSection)) {
  348. return '0';
  349. }
  350. if (empty($assetSection)) {
  351. $assetSection = $actionSection;
  352. }
  353. if ($userId === null) {
  354. $user = &JFactory::getUser();
  355. $userId = $user->get('id');
  356. }
  357. // Generate unique cache id.
  358. $cacheId = $actionSection.'-'.$action.'-'.$userId.'-'.$asString;
  359. // Simple cache
  360. if ($cache == null) {
  361. $cache = array();
  362. }
  363. if (!isset($cache[$cacheId]))
  364. {
  365. $db = &JFactory::getDBO();
  366. $query = new JXQuery;
  367. $query->select('GROUP_CONCAT(DISTINCT axom.value SEPARATOR \',\')');
  368. $query->from('jos_core_acl_aco_map AS am');
  369. $query->join('INNER', '#__core_acl_acl AS acl ON acl.id = am.acl_id');
  370. $query->join('INNER', '#__core_acl_aro_groups_map AS agm ON agm.acl_id = am.acl_id');
  371. $query->join('LEFT', 'jos_core_acl_axo_map AS axom ON axom.acl_id = am.acl_id');
  372. $query->join('INNER', 'jos_core_acl_groups_aro_map AS garom ON garom.group_id = agm.group_id');
  373. $query->join('INNER', 'jos_core_acl_aro AS aro ON aro.id = garom.aro_id');
  374. $query->where('am.section_value = '.$db->Quote($actionSection));
  375. if (is_array($action))
  376. {
  377. $action = array_map(array($db, 'Quote'), $action);
  378. $query->where('am.value IN ('.implode(',', $action).')');
  379. }
  380. else {
  381. $query->where('am.value = '.$db->Quote($action));
  382. }
  383. $query->where('acl.enabled = 1');
  384. $query->where('acl.allow = 1');
  385. $query->where('aro.value = '.(int) $userId);
  386. $db->setQuery($query->toString());
  387. if ($ids = $db->loadResult()) {
  388. if ($asString) {
  389. $cache[$cacheId] = $ids;
  390. }
  391. else {
  392. $cache[$cacheId] = explode(',', $ids);
  393. }
  394. }
  395. else {
  396. if ($asString) {
  397. $cache[$cacheId] = '0';
  398. }
  399. else {
  400. $cache[$cacheId] = array(0);
  401. }
  402. }
  403. }
  404. return $cache[$cacheId];
  405. }
  406. }