Addition to AuthMananger

As far as I can see, CAuth manager handles the question "What can this user do?", but not "Who can do this?" - i.e. given a role, task, or operation, which user(s) can do it?

Use case:

Let’s say the RBAC hierarchy looks something like:

authItem = admin, itemManager (can update and delete items), itemEditor (can only update items)

authItemChild = (parent=>admin, child=>itemManager), (parent=>itemManger, child=>itemEditor), i.e. all admin are also itemManager and all itemManager are also itemEditor.

In authAssigment we assign the userids to the various roles.

Suppose we want to send a notification to everyone that is authorised to do something with an item (edit it, change its status, etc.) when it is posted to our blog; it would be useful to be able to find out all the users that are authorised do this using the lowest authorisation level - in this example "itemEditor".

Below is a class that extends CDbAuthManager that allows you to do this; perhaps something like this should be in the core (CPhpAuthManager would need something similar)?




<?php

/**

 * EDbAuthManager class file.

 * Extends CDbAuthManager with methods that allow you to:

 * + find out who is authorised to perform the given role/task/operation.

 * Can be limited to just the item(s), or the item and parents,

 * or the item and ancestors.

 * + find the parent(s) items of the given item(s).

 * + find all ancestor items of the given item(s).

 *

 * Use case. When an item is posted you can find out who is authorised to manage the item

 * in order to (for example) notify them by email.

 *

 * @license	http://www.opensource.org/licenses/bsd-license.php The BSD License

 * @author	Chris Yates

 */

/**

 * EDbAuthManager class.

 * Extends CDbAuthManager.

 * @package components

 */

class EDbAuthManager extends CDbAuthManager {

	const ASSIGNMENT_ITEM = 1;

	const ASSIGNMENT_PARENTS = 2;

	const ASSIGNMENT_ANCESTORS = 3;


	/**

	 * Returns the assignments of the specified item.

	 * @param mixed the item name.

	 * This can be either a string or an array; the latter represents a list of item names.

	 * @param integer The level for which assigments should be returned:

	 * for the item only (default), the item and parents, or the item and ancestors.

	 * @return array assignemts of the item to the specified level

	 */

	public function getItemAuthAssignments($itemName, $level = self::ASSIGNMENT_ITEM) {

		if (is_string($itemName)) {

			$itemName = array($itemName);

		}


		$itemNames = array();

		foreach ($itemName as $name) {

			$itemNames[] = $this->db->quoteValue($name);

		} // foreach


		switch ($level) {

			case self::ASSIGNMENT_ANCESTORS:

				$authItems = $this->getItemAncestors($itemName);

				break;

			case self::ASSIGNMENT_PARENTS:

				$authItems = $this->getItemParents($itemName);

				break;

			default:

				$authItems = array();

				break;

		}


		foreach ($authItems as $authItem) {

			$itemNames[] = $this->db->quoteValue($authItem->getName());

		} // foreach


		$sql = "SELECT * FROM {$this->assignmentTable} WHERE itemname IN (" .

				implode(', ', $itemNames) . ')';

		$assignments = array();

		foreach($this->db->createCommand($sql)->queryAll() as $row) {

			$assignments[] = new CAuthAssignment($this, $row['itemname'], $row['userid'],  $row['bizrule'], unserialize($row['data']));

		} // foreach

		return $assignments;

	}


	/**

	 * Returns the parent(s) of the specified item.

	 * @param mixed the child item name.

	 * This can be either a string or an array; the latter represents a list of item names.

	 * @return array all parent items of the child

	 */

	public function getItemParents($itemName) {

		if (is_string($itemName)) {

			$condition = 'child=' . $this->db->quoteValue($itemName);

		}

		else if (is_array($itemName) && $itemName!==array()) {

			foreach ($itemName as &$name) {

				$name = $this->db->quoteValue($name);

			}

			$condition = 'child IN ('.implode(', ',$itemName).')';

		}


		$sql = "SELECT name, type, description, bizrule, data FROM {$this->itemTable}, {$this->itemChildTable} WHERE $condition AND name=parent";


		$parents = array();

		foreach ($this->db->createCommand($sql)->queryAll() as $row) {

			$parents[$row['name']] = new CAuthItem($this, $row['name'], $row['type'],

			$row['description'], $row['bizrule'], unserialize($row['data']));

		}

		return $parents;

	}


	/**

	 * Returns the ancestors of the specified item.

	 * @param mixed the decendant item name.

	 * This can be either a string or an array; the latter represents a list of item names.

	 * @return array all ancestor items of the decendant

	 */

	public function getItemAncestors($itemName) {

		$ancestors = array();

		do {

			$parents = $this->getItemParents($itemName);

			$ancestors = array_merge($ancestors, $parents);

			$itemName = array();

			foreach ($parents as $parentName => $parent) {

				$itemName[] = $parentName;

			} // foreach

		} while (!empty($parents));

		return $ancestors;

	}

}