PageRenderTime 28ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 0ms

/yii/framework/web/auth/CDbAuthManager.php

https://github.com/joshuaswarren/weatherhub
PHP | 595 lines | 373 code | 35 blank | 187 comment | 38 complexity | 1e091716313802a10e82bd1d28bfa708 MD5 | raw file
  1. <?php
  2. /**
  3. * CDbAuthManager class file.
  4. *
  5. * @author Qiang Xue <qiang.xue@gmail.com>
  6. * @link http://www.yiiframework.com/
  7. * @copyright Copyright &copy; 2008-2011 Yii Software LLC
  8. * @license http://www.yiiframework.com/license/
  9. */
  10. /**
  11. * CDbAuthManager represents an authorization manager that stores authorization information in database.
  12. *
  13. * The database connection is specified by {@link connectionID}. And the database schema
  14. * should be as described in "framework/web/auth/*.sql". You may change the names of
  15. * the three tables used to store the authorization data by setting {@link itemTable},
  16. * {@link itemChildTable} and {@link assignmentTable}.
  17. *
  18. * @author Qiang Xue <qiang.xue@gmail.com>
  19. * @version $Id: CDbAuthManager.php 3274 2011-06-15 09:28:16Z mdomba $
  20. * @package system.web.auth
  21. * @since 1.0
  22. */
  23. class CDbAuthManager extends CAuthManager
  24. {
  25. /**
  26. * @var string the ID of the {@link CDbConnection} application component. Defaults to 'db'.
  27. * The database must have the tables as declared in "framework/web/auth/*.sql".
  28. */
  29. public $connectionID='db';
  30. /**
  31. * @var string the name of the table storing authorization items. Defaults to 'AuthItem'.
  32. */
  33. public $itemTable='AuthItem';
  34. /**
  35. * @var string the name of the table storing authorization item hierarchy. Defaults to 'AuthItemChild'.
  36. */
  37. public $itemChildTable='AuthItemChild';
  38. /**
  39. * @var string the name of the table storing authorization item assignments. Defaults to 'AuthAssignment'.
  40. */
  41. public $assignmentTable='AuthAssignment';
  42. /**
  43. * @var CDbConnection the database connection. By default, this is initialized
  44. * automatically as the application component whose ID is indicated as {@link connectionID}.
  45. */
  46. public $db;
  47. private $_usingSqlite;
  48. /**
  49. * Initializes the application component.
  50. * This method overrides the parent implementation by establishing the database connection.
  51. */
  52. public function init()
  53. {
  54. parent::init();
  55. $this->_usingSqlite=!strncmp($this->getDbConnection()->getDriverName(),'sqlite',6);
  56. }
  57. /**
  58. * Performs access check for the specified user.
  59. * @param string $itemName the name of the operation that need access check
  60. * @param mixed $userId the user ID. This should can be either an integer and a string representing
  61. * the unique identifier of a user. See {@link IWebUser::getId}.
  62. * @param array $params name-value pairs that would be passed to biz rules associated
  63. * with the tasks and roles assigned to the user.
  64. * @return boolean whether the operations can be performed by the user.
  65. */
  66. public function checkAccess($itemName,$userId,$params=array())
  67. {
  68. $assignments=$this->getAuthAssignments($userId);
  69. return $this->checkAccessRecursive($itemName,$userId,$params,$assignments);
  70. }
  71. /**
  72. * Performs access check for the specified user.
  73. * This method is internally called by {@link checkAccess}.
  74. * @param string $itemName the name of the operation that need access check
  75. * @param mixed $userId the user ID. This should can be either an integer and a string representing
  76. * the unique identifier of a user. See {@link IWebUser::getId}.
  77. * @param array $params name-value pairs that would be passed to biz rules associated
  78. * with the tasks and roles assigned to the user.
  79. * @param array $assignments the assignments to the specified user
  80. * @return boolean whether the operations can be performed by the user.
  81. * @since 1.1.3
  82. */
  83. protected function checkAccessRecursive($itemName,$userId,$params,$assignments)
  84. {
  85. if(($item=$this->getAuthItem($itemName))===null)
  86. return false;
  87. Yii::trace('Checking permission "'.$item->getName().'"','system.web.auth.CDbAuthManager');
  88. if($this->executeBizRule($item->getBizRule(),$params,$item->getData()))
  89. {
  90. if(in_array($itemName,$this->defaultRoles))
  91. return true;
  92. if(isset($assignments[$itemName]))
  93. {
  94. $assignment=$assignments[$itemName];
  95. if($this->executeBizRule($assignment->getBizRule(),$params,$assignment->getData()))
  96. return true;
  97. }
  98. $parents=$this->db->createCommand()
  99. ->select('parent')
  100. ->from($this->itemChildTable)
  101. ->where('child=:name', array(':name'=>$itemName))
  102. ->queryColumn();
  103. foreach($parents as $parent)
  104. {
  105. if($this->checkAccessRecursive($parent,$userId,$params,$assignments))
  106. return true;
  107. }
  108. }
  109. return false;
  110. }
  111. /**
  112. * Adds an item as a child of another item.
  113. * @param string $itemName the parent item name
  114. * @param string $childName the child item name
  115. * @throws CException if either parent or child doesn't exist or if a loop has been detected.
  116. */
  117. public function addItemChild($itemName,$childName)
  118. {
  119. if($itemName===$childName)
  120. throw new CException(Yii::t('yii','Cannot add "{name}" as a child of itself.',
  121. array('{name}'=>$itemName)));
  122. $rows=$this->db->createCommand()
  123. ->select()
  124. ->from($this->itemTable)
  125. ->where('name=:name1 OR name=:name2', array(
  126. ':name1'=>$itemName,
  127. ':name2'=>$childName
  128. ))
  129. ->queryAll();
  130. if(count($rows)==2)
  131. {
  132. if($rows[0]['name']===$itemName)
  133. {
  134. $parentType=$rows[0]['type'];
  135. $childType=$rows[1]['type'];
  136. }
  137. else
  138. {
  139. $childType=$rows[0]['type'];
  140. $parentType=$rows[1]['type'];
  141. }
  142. $this->checkItemChildType($parentType,$childType);
  143. if($this->detectLoop($itemName,$childName))
  144. throw new CException(Yii::t('yii','Cannot add "{child}" as a child of "{name}". A loop has been detected.',
  145. array('{child}'=>$childName,'{name}'=>$itemName)));
  146. $this->db->createCommand()
  147. ->insert($this->itemChildTable, array(
  148. 'parent'=>$itemName,
  149. 'child'=>$childName,
  150. ));
  151. }
  152. else
  153. throw new CException(Yii::t('yii','Either "{parent}" or "{child}" does not exist.',array('{child}'=>$childName,'{parent}'=>$itemName)));
  154. }
  155. /**
  156. * Removes a child from its parent.
  157. * Note, the child item is not deleted. Only the parent-child relationship is removed.
  158. * @param string $itemName the parent item name
  159. * @param string $childName the child item name
  160. * @return boolean whether the removal is successful
  161. */
  162. public function removeItemChild($itemName,$childName)
  163. {
  164. return $this->db->createCommand()
  165. ->delete($this->itemChildTable, 'parent=:parent AND child=:child', array(
  166. ':parent'=>$itemName,
  167. ':child'=>$childName
  168. )) > 0;
  169. }
  170. /**
  171. * Returns a value indicating whether a child exists within a parent.
  172. * @param string $itemName the parent item name
  173. * @param string $childName the child item name
  174. * @return boolean whether the child exists
  175. */
  176. public function hasItemChild($itemName,$childName)
  177. {
  178. return $this->db->createCommand()
  179. ->select('parent')
  180. ->from($this->itemChildTable)
  181. ->where('parent=:parent AND child=:child', array(
  182. ':parent'=>$itemName,
  183. ':child'=>$childName))
  184. ->queryScalar() !== false;
  185. }
  186. /**
  187. * Returns the children of the specified item.
  188. * @param mixed $names the parent item name. This can be either a string or an array.
  189. * The latter represents a list of item names (available since version 1.0.5).
  190. * @return array all child items of the parent
  191. */
  192. public function getItemChildren($names)
  193. {
  194. if(is_string($names))
  195. $condition='parent='.$this->db->quoteValue($names);
  196. else if(is_array($names) && $names!==array())
  197. {
  198. foreach($names as &$name)
  199. $name=$this->db->quoteValue($name);
  200. $condition='parent IN ('.implode(', ',$names).')';
  201. }
  202. $rows=$this->db->createCommand()
  203. ->select('name, type, description, bizrule, data')
  204. ->from(array(
  205. $this->itemTable,
  206. $this->itemChildTable
  207. ))
  208. ->where($condition.' AND name=child')
  209. ->queryAll();
  210. $children=array();
  211. foreach($rows as $row)
  212. {
  213. if(($data=@unserialize($row['data']))===false)
  214. $data=null;
  215. $children[$row['name']]=new CAuthItem($this,$row['name'],$row['type'],$row['description'],$row['bizrule'],$data);
  216. }
  217. return $children;
  218. }
  219. /**
  220. * Assigns an authorization item to a user.
  221. * @param string $itemName the item name
  222. * @param mixed $userId the user ID (see {@link IWebUser::getId})
  223. * @param string $bizRule the business rule to be executed when {@link checkAccess} is called
  224. * for this particular authorization item.
  225. * @param mixed $data additional data associated with this assignment
  226. * @return CAuthAssignment the authorization assignment information.
  227. * @throws CException if the item does not exist or if the item has already been assigned to the user
  228. */
  229. public function assign($itemName,$userId,$bizRule=null,$data=null)
  230. {
  231. if($this->usingSqlite() && $this->getAuthItem($itemName)===null)
  232. throw new CException(Yii::t('yii','The item "{name}" does not exist.',array('{name}'=>$itemName)));
  233. $this->db->createCommand()
  234. ->insert($this->assignmentTable, array(
  235. 'itemname'=>$itemName,
  236. 'userid'=>$userId,
  237. 'bizrule'=>$bizRule,
  238. 'data'=>serialize($data)
  239. ));
  240. return new CAuthAssignment($this,$itemName,$userId,$bizRule,$data);
  241. }
  242. /**
  243. * Revokes an authorization assignment from a user.
  244. * @param string $itemName the item name
  245. * @param mixed $userId the user ID (see {@link IWebUser::getId})
  246. * @return boolean whether removal is successful
  247. */
  248. public function revoke($itemName,$userId)
  249. {
  250. return $this->db->createCommand()
  251. ->delete($this->assignmentTable, 'itemname=:itemname AND userid=:userid', array(
  252. ':itemname'=>$itemName,
  253. ':userid'=>$userId
  254. )) > 0;
  255. }
  256. /**
  257. * Returns a value indicating whether the item has been assigned to the user.
  258. * @param string $itemName the item name
  259. * @param mixed $userId the user ID (see {@link IWebUser::getId})
  260. * @return boolean whether the item has been assigned to the user.
  261. */
  262. public function isAssigned($itemName,$userId)
  263. {
  264. return $this->db->createCommand()
  265. ->select('itemname')
  266. ->from($this->assignmentTable)
  267. ->where('itemname=:itemname AND userid=:userid', array(
  268. ':itemname'=>$itemName,
  269. ':userid'=>$userId))
  270. ->queryScalar() !== false;
  271. }
  272. /**
  273. * Returns the item assignment information.
  274. * @param string $itemName the item name
  275. * @param mixed $userId the user ID (see {@link IWebUser::getId})
  276. * @return CAuthAssignment the item assignment information. Null is returned if
  277. * the item is not assigned to the user.
  278. */
  279. public function getAuthAssignment($itemName,$userId)
  280. {
  281. $row=$this->db->createCommand()
  282. ->select()
  283. ->from($this->assignmentTable)
  284. ->where('itemname=:itemname AND userid=:userid', array(
  285. ':itemname'=>$itemName,
  286. ':userid'=>$userId))
  287. ->queryRow();
  288. if($row!==false)
  289. {
  290. if(($data=@unserialize($row['data']))===false)
  291. $data=null;
  292. return new CAuthAssignment($this,$row['itemname'],$row['userid'],$row['bizrule'],$data);
  293. }
  294. else
  295. return null;
  296. }
  297. /**
  298. * Returns the item assignments for the specified user.
  299. * @param mixed $userId the user ID (see {@link IWebUser::getId})
  300. * @return array the item assignment information for the user. An empty array will be
  301. * returned if there is no item assigned to the user.
  302. */
  303. public function getAuthAssignments($userId)
  304. {
  305. $rows=$this->db->createCommand()
  306. ->select()
  307. ->from($this->assignmentTable)
  308. ->where('userid=:userid', array(':userid'=>$userId))
  309. ->queryAll();
  310. $assignments=array();
  311. foreach($rows as $row)
  312. {
  313. if(($data=@unserialize($row['data']))===false)
  314. $data=null;
  315. $assignments[$row['itemname']]=new CAuthAssignment($this,$row['itemname'],$row['userid'],$row['bizrule'],$data);
  316. }
  317. return $assignments;
  318. }
  319. /**
  320. * Saves the changes to an authorization assignment.
  321. * @param CAuthAssignment $assignment the assignment that has been changed.
  322. */
  323. public function saveAuthAssignment($assignment)
  324. {
  325. $this->db->createCommand()
  326. ->update($this->assignmentTable, array(
  327. 'bizrule'=>$assignment->getBizRule(),
  328. 'data'=>serialize($assignment->getData()),
  329. ), 'itemname=:itemname AND userid=:userid', array(
  330. 'itemname'=>$assignment->getItemName(),
  331. 'userid'=>$assignment->getUserId()
  332. ));
  333. }
  334. /**
  335. * Returns the authorization items of the specific type and user.
  336. * @param integer $type the item type (0: operation, 1: task, 2: role). Defaults to null,
  337. * meaning returning all items regardless of their type.
  338. * @param mixed $userId the user ID. Defaults to null, meaning returning all items even if
  339. * they are not assigned to a user.
  340. * @return array the authorization items of the specific type.
  341. */
  342. public function getAuthItems($type=null,$userId=null)
  343. {
  344. if($type===null && $userId===null)
  345. {
  346. $command=$this->db->createCommand()
  347. ->select()
  348. ->from($this->itemTable);
  349. }
  350. else if($userId===null)
  351. {
  352. $command=$this->db->createCommand()
  353. ->select()
  354. ->from($this->itemTable)
  355. ->where('type=:type', array(':type'=>$type));
  356. }
  357. else if($type===null)
  358. {
  359. $command=$this->db->createCommand()
  360. ->select('name,type,description,t1.bizrule,t1.data')
  361. ->from(array(
  362. $this->itemTable.' t1',
  363. $this->assignmentTable.' t2'
  364. ))
  365. ->where('name=itemname AND userid=:userid', array(':userid'=>$userId));
  366. }
  367. else
  368. {
  369. $command=$this->db->createCommand()
  370. ->select('name,type,description,t1.bizrule,t1.data')
  371. ->from(array(
  372. $this->itemTable.' t1',
  373. $this->assignmentTable.' t2'
  374. ))
  375. ->where('name=itemname AND type=:type AND userid=:userid', array(
  376. ':type'=>$type,
  377. ':userid'=>$userId
  378. ));
  379. }
  380. $items=array();
  381. foreach($command->queryAll() as $row)
  382. {
  383. if(($data=@unserialize($row['data']))===false)
  384. $data=null;
  385. $items[$row['name']]=new CAuthItem($this,$row['name'],$row['type'],$row['description'],$row['bizrule'],$data);
  386. }
  387. return $items;
  388. }
  389. /**
  390. * Creates an authorization item.
  391. * An authorization item represents an action permission (e.g. creating a post).
  392. * It has three types: operation, task and role.
  393. * Authorization items form a hierarchy. Higher level items inheirt permissions representing
  394. * by lower level items.
  395. * @param string $name the item name. This must be a unique identifier.
  396. * @param integer $type the item type (0: operation, 1: task, 2: role).
  397. * @param string $description description of the item
  398. * @param string $bizRule business rule associated with the item. This is a piece of
  399. * PHP code that will be executed when {@link checkAccess} is called for the item.
  400. * @param mixed $data additional data associated with the item.
  401. * @return CAuthItem the authorization item
  402. * @throws CException if an item with the same name already exists
  403. */
  404. public function createAuthItem($name,$type,$description='',$bizRule=null,$data=null)
  405. {
  406. $this->db->createCommand()
  407. ->insert($this->itemTable, array(
  408. 'name'=>$name,
  409. 'type'=>$type,
  410. 'description'=>$description,
  411. 'bizrule'=>$bizRule,
  412. 'data'=>serialize($data)
  413. ));
  414. return new CAuthItem($this,$name,$type,$description,$bizRule,$data);
  415. }
  416. /**
  417. * Removes the specified authorization item.
  418. * @param string $name the name of the item to be removed
  419. * @return boolean whether the item exists in the storage and has been removed
  420. */
  421. public function removeAuthItem($name)
  422. {
  423. if($this->usingSqlite())
  424. {
  425. $this->db->createCommand()
  426. ->delete($this->itemChildTable, 'parent=:name1 OR child=:name2', array(
  427. ':name1'=>$name,
  428. ':name2'=>$name
  429. ));
  430. $this->db->createCommand()
  431. ->delete($this->assignmentTable, 'itemname=:name', array(
  432. ':name'=>$name,
  433. ));
  434. }
  435. return $this->db->createCommand()
  436. ->delete($this->itemTable, 'name=:name', array(
  437. ':name'=>$name
  438. )) > 0;
  439. }
  440. /**
  441. * Returns the authorization item with the specified name.
  442. * @param string $name the name of the item
  443. * @return CAuthItem the authorization item. Null if the item cannot be found.
  444. */
  445. public function getAuthItem($name)
  446. {
  447. $row=$this->db->createCommand()
  448. ->select()
  449. ->from($this->itemTable)
  450. ->where('name=:name', array(':name'=>$name))
  451. ->queryRow();
  452. if($row!==false)
  453. {
  454. if(($data=@unserialize($row['data']))===false)
  455. $data=null;
  456. return new CAuthItem($this,$row['name'],$row['type'],$row['description'],$row['bizrule'],$data);
  457. }
  458. else
  459. return null;
  460. }
  461. /**
  462. * Saves an authorization item to persistent storage.
  463. * @param CAuthItem $item the item to be saved.
  464. * @param string $oldName the old item name. If null, it means the item name is not changed.
  465. */
  466. public function saveAuthItem($item,$oldName=null)
  467. {
  468. if($this->usingSqlite() && $oldName!==null && $item->getName()!==$oldName)
  469. {
  470. $this->db->createCommand()
  471. ->update($this->itemChildTable, array(
  472. 'parent'=>$item->getName(),
  473. ), 'parent=:whereName', array(
  474. ':whereName'=>$oldName,
  475. ));
  476. $this->db->createCommand()
  477. ->update($this->itemChildTable, array(
  478. 'child'=>$item->getName(),
  479. ), 'child=:whereName', array(
  480. ':whereName'=>$oldName,
  481. ));
  482. $this->db->createCommand()
  483. ->update($this->assignmentTable, array(
  484. 'itemname'=>$item->getName(),
  485. ), 'itemname=:whereName', array(
  486. ':whereName'=>$oldName,
  487. ));
  488. }
  489. $this->db->createCommand()
  490. ->update($this->itemTable, array(
  491. 'name'=>$item->getName(),
  492. 'type'=>$item->getType(),
  493. 'description'=>$item->getDescription(),
  494. 'bizrule'=>$item->getBizRule(),
  495. 'data'=>serialize($item->getData()),
  496. ), 'name=:whereName', array(
  497. ':whereName'=>$oldName===null?$item->getName():$oldName,
  498. ));
  499. }
  500. /**
  501. * Saves the authorization data to persistent storage.
  502. */
  503. public function save()
  504. {
  505. }
  506. /**
  507. * Removes all authorization data.
  508. */
  509. public function clearAll()
  510. {
  511. $this->clearAuthAssignments();
  512. $this->db->createCommand()->delete($this->itemChildTable);
  513. $this->db->createCommand()->delete($this->itemTable);
  514. }
  515. /**
  516. * Removes all authorization assignments.
  517. */
  518. public function clearAuthAssignments()
  519. {
  520. $this->db->createCommand()->delete($this->assignmentTable);
  521. }
  522. /**
  523. * Checks whether there is a loop in the authorization item hierarchy.
  524. * @param string $itemName parent item name
  525. * @param string $childName the name of the child item that is to be added to the hierarchy
  526. * @return boolean whether a loop exists
  527. */
  528. protected function detectLoop($itemName,$childName)
  529. {
  530. if($childName===$itemName)
  531. return true;
  532. foreach($this->getItemChildren($childName) as $child)
  533. {
  534. if($this->detectLoop($itemName,$child->getName()))
  535. return true;
  536. }
  537. return false;
  538. }
  539. /**
  540. * @return CDbConnection the DB connection instance
  541. * @throws CException if {@link connectionID} does not point to a valid application component.
  542. */
  543. protected function getDbConnection()
  544. {
  545. if($this->db!==null)
  546. return $this->db;
  547. else if(($this->db=Yii::app()->getComponent($this->connectionID)) instanceof CDbConnection)
  548. return $this->db;
  549. else
  550. throw new CException(Yii::t('yii','CDbAuthManager.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.',
  551. array('{id}'=>$this->connectionID)));
  552. }
  553. /**
  554. * @return boolean whether the database is a SQLite database
  555. */
  556. protected function usingSqlite()
  557. {
  558. return $this->_usingSqlite;
  559. }
  560. }