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;
}
}