CPhpAuthManager.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. <?php
  2. /**
  3. * CPhpAuthManager class file.
  4. *
  5. * @author Qiang Xue <qiang.xue@gmail.com>
  6. * @link http://www.yiiframework.com/
  7. * @copyright 2008-2013 Yii Software LLC
  8. * @license http://www.yiiframework.com/license/
  9. */
  10. /**
  11. * CPhpAuthManager represents an authorization manager that stores authorization information in terms of a PHP script file.
  12. *
  13. * The authorization data will be saved to and loaded from a file
  14. * specified by {@link authFile}, which defaults to 'protected/data/auth.php'.
  15. *
  16. * CPhpAuthManager is mainly suitable for authorization data that is not too big
  17. * (for example, the authorization data for a personal blog system).
  18. * Use {@link CDbAuthManager} for more complex authorization data.
  19. *
  20. * @property array $authItems The authorization items of the specific type.
  21. *
  22. * @author Qiang Xue <qiang.xue@gmail.com>
  23. * @package system.web.auth
  24. * @since 1.0
  25. */
  26. class CPhpAuthManager extends CAuthManager
  27. {
  28. /**
  29. * @var string the path of the PHP script that contains the authorization data.
  30. * If not set, it will be using 'protected/data/auth.php' as the data file.
  31. * Make sure this file is writable by the Web server process if the authorization
  32. * needs to be changed.
  33. * @see loadFromFile
  34. * @see saveToFile
  35. */
  36. public $authFile;
  37. private $_items=array(); // itemName => item
  38. private $_children=array(); // itemName, childName => child
  39. private $_assignments=array(); // userId, itemName => assignment
  40. /**
  41. * Initializes the application component.
  42. * This method overrides parent implementation by loading the authorization data
  43. * from PHP script.
  44. */
  45. public function init()
  46. {
  47. parent::init();
  48. if($this->authFile===null)
  49. $this->authFile=Yii::getPathOfAlias('application.data.auth').'.php';
  50. $this->load();
  51. }
  52. /**
  53. * Performs access check for the specified user.
  54. * @param string $itemName the name of the operation that need access check
  55. * @param mixed $userId the user ID. This can be either an integer or a string representing
  56. * the unique identifier of a user. See {@link IWebUser::getId}.
  57. * @param array $params name-value pairs that would be passed to biz rules associated
  58. * with the tasks and roles assigned to the user.
  59. * Since version 1.1.11 a param with name 'userId' is added to this array, which holds the value of <code>$userId</code>.
  60. * @return boolean whether the operations can be performed by the user.
  61. */
  62. public function checkAccess($itemName,$userId,$params=array())
  63. {
  64. if(!isset($this->_items[$itemName]))
  65. return false;
  66. $item=$this->_items[$itemName];
  67. Yii::trace('Checking permission "'.$item->getName().'"','system.web.auth.CPhpAuthManager');
  68. if(!isset($params['userId']))
  69. $params['userId'] = $userId;
  70. if($this->executeBizRule($item->getBizRule(),$params,$item->getData()))
  71. {
  72. if(in_array($itemName,$this->defaultRoles))
  73. return true;
  74. if(isset($this->_assignments[$userId][$itemName]))
  75. {
  76. $assignment=$this->_assignments[$userId][$itemName];
  77. if($this->executeBizRule($assignment->getBizRule(),$params,$assignment->getData()))
  78. return true;
  79. }
  80. foreach($this->_children as $parentName=>$children)
  81. {
  82. if(isset($children[$itemName]) && $this->checkAccess($parentName,$userId,$params))
  83. return true;
  84. }
  85. }
  86. return false;
  87. }
  88. /**
  89. * Adds an item as a child of another item.
  90. * @param string $itemName the parent item name
  91. * @param string $childName the child item name
  92. * @return boolean whether the item is added successfully
  93. * @throws CException if either parent or child doesn't exist or if a loop has been detected.
  94. */
  95. public function addItemChild($itemName,$childName)
  96. {
  97. if(!isset($this->_items[$childName],$this->_items[$itemName]))
  98. throw new CException(Yii::t('yii','Either "{parent}" or "{child}" does not exist.',array('{child}'=>$childName,'{parent}'=>$itemName)));
  99. $child=$this->_items[$childName];
  100. $item=$this->_items[$itemName];
  101. $this->checkItemChildType($item->getType(),$child->getType());
  102. if($this->detectLoop($itemName,$childName))
  103. throw new CException(Yii::t('yii','Cannot add "{child}" as a child of "{parent}". A loop has been detected.',
  104. array('{child}'=>$childName,'{parent}'=>$itemName)));
  105. if(isset($this->_children[$itemName][$childName]))
  106. throw new CException(Yii::t('yii','The item "{parent}" already has a child "{child}".',
  107. array('{child}'=>$childName,'{parent}'=>$itemName)));
  108. $this->_children[$itemName][$childName]=$this->_items[$childName];
  109. return true;
  110. }
  111. /**
  112. * Removes a child from its parent.
  113. * Note, the child item is not deleted. Only the parent-child relationship is removed.
  114. * @param string $itemName the parent item name
  115. * @param string $childName the child item name
  116. * @return boolean whether the removal is successful
  117. */
  118. public function removeItemChild($itemName,$childName)
  119. {
  120. if(isset($this->_children[$itemName][$childName]))
  121. {
  122. unset($this->_children[$itemName][$childName]);
  123. return true;
  124. }
  125. else
  126. return false;
  127. }
  128. /**
  129. * Returns a value indicating whether a child exists within a parent.
  130. * @param string $itemName the parent item name
  131. * @param string $childName the child item name
  132. * @return boolean whether the child exists
  133. */
  134. public function hasItemChild($itemName,$childName)
  135. {
  136. return isset($this->_children[$itemName][$childName]);
  137. }
  138. /**
  139. * Returns the children of the specified item.
  140. * @param mixed $names the parent item name. This can be either a string or an array.
  141. * The latter represents a list of item names.
  142. * @return array all child items of the parent
  143. */
  144. public function getItemChildren($names)
  145. {
  146. if(is_string($names))
  147. return isset($this->_children[$names]) ? $this->_children[$names] : array();
  148. $children=array();
  149. foreach($names as $name)
  150. {
  151. if(isset($this->_children[$name]))
  152. $children=array_merge($children,$this->_children[$name]);
  153. }
  154. return $children;
  155. }
  156. /**
  157. * Assigns an authorization item to a user.
  158. * @param string $itemName the item name
  159. * @param mixed $userId the user ID (see {@link IWebUser::getId})
  160. * @param string $bizRule the business rule to be executed when {@link checkAccess} is called
  161. * for this particular authorization item.
  162. * @param mixed $data additional data associated with this assignment
  163. * @return CAuthAssignment the authorization assignment information.
  164. * @throws CException if the item does not exist or if the item has already been assigned to the user
  165. */
  166. public function assign($itemName,$userId,$bizRule=null,$data=null)
  167. {
  168. if(!isset($this->_items[$itemName]))
  169. throw new CException(Yii::t('yii','Unknown authorization item "{name}".',array('{name}'=>$itemName)));
  170. elseif(isset($this->_assignments[$userId][$itemName]))
  171. throw new CException(Yii::t('yii','Authorization item "{item}" has already been assigned to user "{user}".',
  172. array('{item}'=>$itemName,'{user}'=>$userId)));
  173. else
  174. return $this->_assignments[$userId][$itemName]=new CAuthAssignment($this,$itemName,$userId,$bizRule,$data);
  175. }
  176. /**
  177. * Revokes an authorization assignment from a user.
  178. * @param string $itemName the item name
  179. * @param mixed $userId the user ID (see {@link IWebUser::getId})
  180. * @return boolean whether removal is successful
  181. */
  182. public function revoke($itemName,$userId)
  183. {
  184. if(isset($this->_assignments[$userId][$itemName]))
  185. {
  186. unset($this->_assignments[$userId][$itemName]);
  187. return true;
  188. }
  189. else
  190. return false;
  191. }
  192. /**
  193. * Returns a value indicating whether the item has been assigned to the user.
  194. * @param string $itemName the item name
  195. * @param mixed $userId the user ID (see {@link IWebUser::getId})
  196. * @return boolean whether the item has been assigned to the user.
  197. */
  198. public function isAssigned($itemName,$userId)
  199. {
  200. return isset($this->_assignments[$userId][$itemName]);
  201. }
  202. /**
  203. * Returns the item assignment information.
  204. * @param string $itemName the item name
  205. * @param mixed $userId the user ID (see {@link IWebUser::getId})
  206. * @return CAuthAssignment the item assignment information. Null is returned if
  207. * the item is not assigned to the user.
  208. */
  209. public function getAuthAssignment($itemName,$userId)
  210. {
  211. return isset($this->_assignments[$userId][$itemName])?$this->_assignments[$userId][$itemName]:null;
  212. }
  213. /**
  214. * Returns the item assignments for the specified user.
  215. * @param mixed $userId the user ID (see {@link IWebUser::getId})
  216. * @return array the item assignment information for the user. An empty array will be
  217. * returned if there is no item assigned to the user.
  218. */
  219. public function getAuthAssignments($userId)
  220. {
  221. return isset($this->_assignments[$userId])?$this->_assignments[$userId]:array();
  222. }
  223. /**
  224. * Returns the authorization items of the specific type and user.
  225. * @param integer $type the item type (0: operation, 1: task, 2: role). Defaults to null,
  226. * meaning returning all items regardless of their type.
  227. * @param mixed $userId the user ID. Defaults to null, meaning returning all items even if
  228. * they are not assigned to a user.
  229. * @return array the authorization items of the specific type.
  230. */
  231. public function getAuthItems($type=null,$userId=null)
  232. {
  233. if($type===null && $userId===null)
  234. return $this->_items;
  235. $items=array();
  236. if($userId===null)
  237. {
  238. foreach($this->_items as $name=>$item)
  239. {
  240. if($item->getType()==$type)
  241. $items[$name]=$item;
  242. }
  243. }
  244. elseif(isset($this->_assignments[$userId]))
  245. {
  246. foreach($this->_assignments[$userId] as $assignment)
  247. {
  248. $name=$assignment->getItemName();
  249. if(isset($this->_items[$name]) && ($type===null || $this->_items[$name]->getType()==$type))
  250. $items[$name]=$this->_items[$name];
  251. }
  252. }
  253. return $items;
  254. }
  255. /**
  256. * Creates an authorization item.
  257. * An authorization item represents an action permission (e.g. creating a post).
  258. * It has three types: operation, task and role.
  259. * Authorization items form a hierarchy. Higher level items inherit permissions representing
  260. * by lower level items.
  261. * @param string $name the item name. This must be a unique identifier.
  262. * @param integer $type the item type (0: operation, 1: task, 2: role).
  263. * @param string $description description of the item
  264. * @param string $bizRule business rule associated with the item. This is a piece of
  265. * PHP code that will be executed when {@link checkAccess} is called for the item.
  266. * @param mixed $data additional data associated with the item.
  267. * @return CAuthItem the authorization item
  268. * @throws CException if an item with the same name already exists
  269. */
  270. public function createAuthItem($name,$type,$description='',$bizRule=null,$data=null)
  271. {
  272. if(isset($this->_items[$name]))
  273. throw new CException(Yii::t('yii','Unable to add an item whose name is the same as an existing item.'));
  274. return $this->_items[$name]=new CAuthItem($this,$name,$type,$description,$bizRule,$data);
  275. }
  276. /**
  277. * Removes the specified authorization item.
  278. * @param string $name the name of the item to be removed
  279. * @return boolean whether the item exists in the storage and has been removed
  280. */
  281. public function removeAuthItem($name)
  282. {
  283. if(isset($this->_items[$name]))
  284. {
  285. foreach($this->_children as &$children)
  286. unset($children[$name]);
  287. foreach($this->_assignments as &$assignments)
  288. unset($assignments[$name]);
  289. unset($this->_items[$name]);
  290. return true;
  291. }
  292. else
  293. return false;
  294. }
  295. /**
  296. * Returns the authorization item with the specified name.
  297. * @param string $name the name of the item
  298. * @return CAuthItem the authorization item. Null if the item cannot be found.
  299. */
  300. public function getAuthItem($name)
  301. {
  302. return isset($this->_items[$name])?$this->_items[$name]:null;
  303. }
  304. /**
  305. * Saves an authorization item to persistent storage.
  306. * @param CAuthItem $item the item to be saved.
  307. * @param string $oldName the old item name. If null, it means the item name is not changed.
  308. */
  309. public function saveAuthItem($item,$oldName=null)
  310. {
  311. if($oldName!==null && ($newName=$item->getName())!==$oldName) // name changed
  312. {
  313. if(isset($this->_items[$newName]))
  314. throw new CException(Yii::t('yii','Unable to change the item name. The name "{name}" is already used by another item.',array('{name}'=>$newName)));
  315. if(isset($this->_items[$oldName]) && $this->_items[$oldName]===$item)
  316. {
  317. unset($this->_items[$oldName]);
  318. $this->_items[$newName]=$item;
  319. if(isset($this->_children[$oldName]))
  320. {
  321. $this->_children[$newName]=$this->_children[$oldName];
  322. unset($this->_children[$oldName]);
  323. }
  324. foreach($this->_children as &$children)
  325. {
  326. if(isset($children[$oldName]))
  327. {
  328. $children[$newName]=$children[$oldName];
  329. unset($children[$oldName]);
  330. }
  331. }
  332. foreach($this->_assignments as &$assignments)
  333. {
  334. if(isset($assignments[$oldName]))
  335. {
  336. $assignments[$newName]=$assignments[$oldName];
  337. unset($assignments[$oldName]);
  338. }
  339. }
  340. }
  341. }
  342. }
  343. /**
  344. * Saves the changes to an authorization assignment.
  345. * @param CAuthAssignment $assignment the assignment that has been changed.
  346. */
  347. public function saveAuthAssignment($assignment)
  348. {
  349. }
  350. /**
  351. * Saves authorization data into persistent storage.
  352. * If any change is made to the authorization data, please make
  353. * sure you call this method to save the changed data into persistent storage.
  354. */
  355. public function save()
  356. {
  357. $items=array();
  358. foreach($this->_items as $name=>$item)
  359. {
  360. $items[$name]=array(
  361. 'type'=>$item->getType(),
  362. 'description'=>$item->getDescription(),
  363. 'bizRule'=>$item->getBizRule(),
  364. 'data'=>$item->getData(),
  365. );
  366. if(isset($this->_children[$name]))
  367. {
  368. foreach($this->_children[$name] as $child)
  369. $items[$name]['children'][]=$child->getName();
  370. }
  371. }
  372. foreach($this->_assignments as $userId=>$assignments)
  373. {
  374. foreach($assignments as $name=>$assignment)
  375. {
  376. if(isset($items[$name]))
  377. {
  378. $items[$name]['assignments'][$userId]=array(
  379. 'bizRule'=>$assignment->getBizRule(),
  380. 'data'=>$assignment->getData(),
  381. );
  382. }
  383. }
  384. }
  385. $this->saveToFile($items,$this->authFile);
  386. }
  387. /**
  388. * Loads authorization data.
  389. */
  390. public function load()
  391. {
  392. $this->clearAll();
  393. $items=$this->loadFromFile($this->authFile);
  394. foreach($items as $name=>$item)
  395. $this->_items[$name]=new CAuthItem($this,$name,$item['type'],$item['description'],$item['bizRule'],$item['data']);
  396. foreach($items as $name=>$item)
  397. {
  398. if(isset($item['children']))
  399. {
  400. foreach($item['children'] as $childName)
  401. {
  402. if(isset($this->_items[$childName]))
  403. $this->_children[$name][$childName]=$this->_items[$childName];
  404. }
  405. }
  406. if(isset($item['assignments']))
  407. {
  408. foreach($item['assignments'] as $userId=>$assignment)
  409. {
  410. $this->_assignments[$userId][$name]=new CAuthAssignment($this,$name,$userId,$assignment['bizRule'],$assignment['data']);
  411. }
  412. }
  413. }
  414. }
  415. /**
  416. * Removes all authorization data.
  417. */
  418. public function clearAll()
  419. {
  420. $this->clearAuthAssignments();
  421. $this->_children=array();
  422. $this->_items=array();
  423. }
  424. /**
  425. * Removes all authorization assignments.
  426. */
  427. public function clearAuthAssignments()
  428. {
  429. $this->_assignments=array();
  430. }
  431. /**
  432. * Checks whether there is a loop in the authorization item hierarchy.
  433. * @param string $itemName parent item name
  434. * @param string $childName the name of the child item that is to be added to the hierarchy
  435. * @return boolean whether a loop exists
  436. */
  437. protected function detectLoop($itemName,$childName)
  438. {
  439. if($childName===$itemName)
  440. return true;
  441. if(!isset($this->_children[$childName], $this->_items[$itemName]))
  442. return false;
  443. foreach($this->_children[$childName] as $child)
  444. {
  445. if($this->detectLoop($itemName,$child->getName()))
  446. return true;
  447. }
  448. return false;
  449. }
  450. /**
  451. * Loads the authorization data from a PHP script file.
  452. * @param string $file the file path.
  453. * @return array the authorization data
  454. * @see saveToFile
  455. */
  456. protected function loadFromFile($file)
  457. {
  458. if(is_file($file))
  459. return require($file);
  460. else
  461. return array();
  462. }
  463. /**
  464. * Saves the authorization data to a PHP script file.
  465. * @param array $data the authorization data
  466. * @param string $file the file path.
  467. * @see loadFromFile
  468. */
  469. protected function saveToFile($data,$file)
  470. {
  471. file_put_contents($file,"<?php\nreturn ".var_export($data,true).";\n");
  472. }
  473. }