From cd3387067155f34df056629c233fa895dfb1ba83 Mon Sep 17 00:00:00 2001 From: Alexander Kochetov Date: Thu, 9 May 2013 18:53:17 +0400 Subject: [PATCH] Moved to framework/rbac + method names refactoring --- framework/rbac/Assignment.php | 97 ++++++++ framework/rbac/Item.php | 261 +++++++++++++++++++++ framework/rbac/Manager.php | 143 ++++++++++++ framework/rbac/PhpManager.php | 492 +++++++++++++++++++++++++++++++++++++++ framework/web/AuthAssignment.php | 97 -------- framework/web/AuthItem.php | 261 --------------------- framework/web/AuthManager.php | 143 ------------ framework/web/IAuthManager.php | 173 -------------- 8 files changed, 993 insertions(+), 674 deletions(-) create mode 100644 framework/rbac/Assignment.php create mode 100644 framework/rbac/Item.php create mode 100644 framework/rbac/Manager.php create mode 100644 framework/rbac/PhpManager.php delete mode 100644 framework/web/AuthAssignment.php delete mode 100644 framework/web/AuthItem.php delete mode 100644 framework/web/AuthManager.php delete mode 100644 framework/web/IAuthManager.php diff --git a/framework/rbac/Assignment.php b/framework/rbac/Assignment.php new file mode 100644 index 0000000..c2a9f56 --- /dev/null +++ b/framework/rbac/Assignment.php @@ -0,0 +1,97 @@ + + * @author Alexander Kochetov + * @since 2.0 + */ +class Assignment extends Object +{ + private $_auth; + private $_itemName; + private $_userId; + private $_bizRule; + private $_data; + + /** + * Constructor. + * @param IManager $auth the authorization manager + * @param string $itemName authorization item name + * @param mixed $userId user ID (see [[User::id]]) + * @param string $bizRule the business rule associated with this assignment + * @param mixed $data additional data for this assignment + */ + public function __construct($auth, $itemName, $userId, $bizRule = null, $data = null) + { + $this->_auth = $auth; + $this->_itemName = $itemName; + $this->_userId = $userId; + $this->_bizRule = $bizRule; + $this->_data = $data; + } + + /** + * @return mixed user ID (see [[User::id]]) + */ + public function getUserId() + { + return $this->_userId; + } + + /** + * @return string the authorization item name + */ + public function getItemName() + { + return $this->_itemName; + } + + /** + * @return string the business rule associated with this assignment + */ + public function getBizRule() + { + return $this->_bizRule; + } + + /** + * @param string $value the business rule associated with this assignment + */ + public function setBizRule($value) + { + if ($this->_bizRule !== $value) { + $this->_bizRule = $value; + $this->_auth->saveAssignment($this); + } + } + + /** + * @return mixed additional data for this assignment + */ + public function getData() + { + return $this->_data; + } + + /** + * @param mixed $value additional data for this assignment + */ + public function setData($value) + { + if ($this->_data !== $value) { + $this->_data = $value; + $this->_auth->saveAssignment($this); + } + } +} diff --git a/framework/rbac/Item.php b/framework/rbac/Item.php new file mode 100644 index 0000000..ba78e60 --- /dev/null +++ b/framework/rbac/Item.php @@ -0,0 +1,261 @@ + + * @author Alexander Kochetov + * @since 2.0 + */ +class Item extends Object +{ + const TYPE_OPERATION = 0; + const TYPE_TASK = 1; + const TYPE_ROLE = 2; + + private $_auth; + private $_type; + private $_name; + private $_description; + private $_bizRule; + private $_data; + + /** + * Constructor. + * @param IManager $auth authorization manager + * @param string $name authorization item name + * @param integer $type authorization item type. This can be 0 (operation), 1 (task) or 2 (role). + * @param string $description the description + * @param string $bizRule the business rule associated with this item + * @param mixed $data additional data for this item + */ + public function __construct($auth, $name, $type, $description = '', $bizRule = null, $data = null) + { + $this->_type = (int)$type; + $this->_auth = $auth; + $this->_name = $name; + $this->_description = $description; + $this->_bizRule = $bizRule; + $this->_data = $data; + } + + /** + * Checks to see if the specified item is within the hierarchy starting from this item. + * This method is expected to be internally used by the actual implementations + * of the [[IManager::checkAccess()]]. + * @param string $itemName the name of the item to be checked + * @param array $params the parameters to be passed to business rule evaluation + * @return boolean whether the specified item is within the hierarchy starting from this item. + */ + public function checkAccess($itemName, $params = array()) + { + Yii::trace('Checking permission: ' . $this->_name, __METHOD__); + if ($this->_auth->executeBizRule($this->_bizRule, $params, $this->_data)) { + if ($this->_name == $itemName) { + return true; + } + foreach ($this->_auth->getItemChildren($this->_name) as $item) { + if ($item->checkAccess($itemName, $params)) { + return true; + } + } + } + return false; + } + + /** + * @return IManager the authorization manager + */ + public function getManager() + { + return $this->_auth; + } + + /** + * @return integer the authorization item type. This could be 0 (operation), 1 (task) or 2 (role). + */ + public function getType() + { + return $this->_type; + } + + /** + * @return string the item name + */ + public function getName() + { + return $this->_name; + } + + /** + * @param string $value the item name + */ + public function setName($value) + { + if ($this->_name !== $value) { + $oldName = $this->_name; + $this->_name = $value; + $this->_auth->saveItem($this, $oldName); + } + } + + /** + * @return string the item description + */ + public function getDescription() + { + return $this->_description; + } + + /** + * @param string $value the item description + */ + public function setDescription($value) + { + if ($this->_description !== $value) { + $this->_description = $value; + $this->_auth->saveItem($this); + } + } + + /** + * @return string the business rule associated with this item + */ + public function getBizRule() + { + return $this->_bizRule; + } + + /** + * @param string $value the business rule associated with this item + */ + public function setBizRule($value) + { + if ($this->_bizRule !== $value) { + $this->_bizRule = $value; + $this->_auth->saveItem($this); + } + } + + /** + * @return mixed the additional data associated with this item + */ + public function getData() + { + return $this->_data; + } + + /** + * @param mixed $value the additional data associated with this item + */ + public function setData($value) + { + if ($this->_data !== $value) { + $this->_data = $value; + $this->_auth->saveItem($this); + } + } + + /** + * Adds a child item. + * @param string $name the name of the child item + * @return boolean whether the item is added successfully + * @throws \yii\base\Exception if either parent or child doesn't exist or if a loop has been detected. + * @see IManager::addItemChild + */ + public function addChild($name) + { + return $this->_auth->addItemChild($this->_name, $name); + } + + /** + * Removes a child item. + * Note, the child item is not deleted. Only the parent-child relationship is removed. + * @param string $name the child item name + * @return boolean whether the removal is successful + * @see IManager::removeItemChild + */ + public function removeChild($name) + { + return $this->_auth->removeItemChild($this->_name, $name); + } + + /** + * Returns a value indicating whether a child exists + * @param string $name the child item name + * @return boolean whether the child exists + * @see IManager::hasItemChild + */ + public function hasChild($name) + { + return $this->_auth->hasItemChild($this->_name, $name); + } + + /** + * Returns the children of this item. + * @return array all child items of this item. + * @see IManager::getItemChildren + */ + public function getChildren() + { + return $this->_auth->getItemChildren($this->_name); + } + + /** + * Assigns this item to a user. + * @param mixed $userId the user ID (see [[User::id]]) + * @param string $bizRule the business rule to be executed when [[checkAccess()]] is called + * for this particular authorization item. + * @param mixed $data additional data associated with this assignment + * @return Assignment the authorization assignment information. + * @throws \yii\base\Exception if the item has already been assigned to the user + * @see IManager::assign + */ + public function assign($userId, $bizRule = null, $data = null) + { + return $this->_auth->assign($userId, $this->_name, $bizRule, $data); + } + + /** + * Revokes an authorization assignment from a user. + * @param mixed $userId the user ID (see [[User::id]]) + * @return boolean whether removal is successful + * @see IManager::revoke + */ + public function revoke($userId) + { + return $this->_auth->revoke($userId, $this->_name); + } + + /** + * Returns a value indicating whether this item has been assigned to the user. + * @param mixed $userId the user ID (see [[User::id]]) + * @return boolean whether the item has been assigned to the user. + * @see IManager::isAssigned + */ + public function isAssigned($userId) + { + return $this->_auth->isAssigned($userId, $this->_name); + } + + /** + * Returns the item assignment information. + * @param mixed $userId the user ID (see [[User::id]]) + * @return Assignment the item assignment information. Null is returned if + * this item is not assigned to the user. + * @see IManager::getAssignment + */ + public function getAssignment($userId) + { + return $this->_auth->getAssignment($userId, $this->_name); + } +} diff --git a/framework/rbac/Manager.php b/framework/rbac/Manager.php new file mode 100644 index 0000000..2f77e10 --- /dev/null +++ b/framework/rbac/Manager.php @@ -0,0 +1,143 @@ + + * @author Alexander Kochetov + * @since 2.0 + */ +abstract class Manager extends Component implements IManager +{ + /** + * @var boolean Enable error reporting for bizRules. + */ + public $showErrors = false; + + /** + * @var array list of role names that are assigned to all users implicitly. + * These roles do not need to be explicitly assigned to any user. + * When calling [[checkAccess()]], these roles will be checked first. + * For performance reason, you should minimize the number of such roles. + * A typical usage of such roles is to define an 'authenticated' role and associate + * it with a biz rule which checks if the current user is authenticated. + * And then declare 'authenticated' in this property so that it can be applied to + * every authenticated user. + */ + public $defaultRoles = array(); + + /** + * Creates a role. + * This is a shortcut method to [[IManager::createItem()]]. + * @param string $name the item name + * @param string $description the item description. + * @param string $bizRule the business rule associated with this item + * @param mixed $data additional data to be passed when evaluating the business rule + * @return Item the authorization item + */ + public function createRole($name, $description = '', $bizRule = null, $data = null) + { + return $this->createItem($name, Item::TYPE_ROLE, $description, $bizRule, $data); + } + + /** + * Creates a task. + * This is a shortcut method to [[IManager::createItem()]]. + * @param string $name the item name + * @param string $description the item description. + * @param string $bizRule the business rule associated with this item + * @param mixed $data additional data to be passed when evaluating the business rule + * @return Item the authorization item + */ + public function createTask($name, $description = '', $bizRule = null, $data = null) + { + return $this->createItem($name, Item::TYPE_TASK, $description, $bizRule, $data); + } + + /** + * Creates an operation. + * This is a shortcut method to [[IManager::createItem()]]. + * @param string $name the item name + * @param string $description the item description. + * @param string $bizRule the business rule associated with this item + * @param mixed $data additional data to be passed when evaluating the business rule + * @return Item the authorization item + */ + public function createOperation($name, $description = '', $bizRule = null, $data = null) + { + return $this->createItem($name, Item::TYPE_OPERATION, $description, $bizRule, $data); + } + + /** + * Returns roles. + * This is a shortcut method to [[IManager::getItems()]]. + * @param mixed $userId the user ID. If not null, only the roles directly assigned to the user + * will be returned. Otherwise, all roles will be returned. + * @return array roles (name=>AuthItem) + */ + public function getRoles($userId = null) + { + return $this->getItems($userId, Item::TYPE_ROLE); + } + + /** + * Returns tasks. + * This is a shortcut method to [[IManager::getItems()]]. + * @param mixed $userId the user ID. If not null, only the tasks directly assigned to the user + * will be returned. Otherwise, all tasks will be returned. + * @return array tasks (name=>AuthItem) + */ + public function getTasks($userId = null) + { + return $this->getItems($userId, Item::TYPE_TASK); + } + + /** + * Returns operations. + * This is a shortcut method to [[IManager::getItems()]]. + * @param mixed $userId the user ID. If not null, only the operations directly assigned to the user + * will be returned. Otherwise, all operations will be returned. + * @return array operations (name=>AuthItem) + */ + public function getOperations($userId = null) + { + return $this->getItems($userId, Item::TYPE_OPERATION); + } + + /** + * Executes the specified business rule. + * @param string $bizRule the business rule to be executed. + * @param array $params parameters passed to [[IManager::checkAccess()]]. + * @param mixed $data additional data associated with the authorization item or assignment. + * @return boolean whether the business rule returns true. + * If the business rule is empty, it will still return true. + */ + public function executeBizRule($bizRule, $params, $data) + { + return $bizRule === '' || $bizRule === null || ($this->showErrors ? eval($bizRule) != 0 : @eval($bizRule) != 0); + } + + /** + * Checks the item types to make sure a child can be added to a parent. + * @param integer $parentType parent item type + * @param integer $childType child item type + * @throws Exception if the item cannot be added as a child due to its incompatible type. + */ + protected function checkItemChildType($parentType, $childType) + { + static $types = array('operation', 'task', 'role'); + if ($parentType < $childType) { + throw new Exception("Cannot add an item of type '$types[$childType]' to an item of type '$types[$parentType]'."); + } + } +} diff --git a/framework/rbac/PhpManager.php b/framework/rbac/PhpManager.php new file mode 100644 index 0000000..08f5c83 --- /dev/null +++ b/framework/rbac/PhpManager.php @@ -0,0 +1,492 @@ + + * @author Alexander Kochetov + * @since 2.0 + */ +class PhpManager extends Manager +{ + /** + * @var string the path of the PHP script that contains the authorization data. + * If not set, it will be using 'protected/data/auth.php' as the data file. + * Make sure this file is writable by the Web server process if the authorization + * needs to be changed. + * @see loadFromFile + * @see saveToFile + */ + public $authFile; + + private $_items = array(); // itemName => item + private $_children = array(); // itemName, childName => child + private $_assignments = array(); // userId, itemName => assignment + + /** + * Initializes the application component. + * This method overrides parent implementation by loading the authorization data + * from PHP script. + */ + public function init() + { + parent::init(); + if ($this->authFile === null) { + $this->authFile = Yii::getAlias('@app/data/rbac') . '.php'; + } + $this->load(); + } + + /** + * Performs access check for the specified user. + * @param mixed $userId the user ID. This can be either an integer or a string representing + * @param string $itemName the name of the operation that need access check + * the unique identifier of a user. See [[User::id]]. + * @param array $params name-value pairs that would be passed to biz rules associated + * with the tasks and roles assigned to the user. + * Since version 1.1.11 a param with name 'userId' is added to this array, which holds the value of $userId. + * @return boolean whether the operations can be performed by the user. + */ + public function checkAccess($userId, $itemName, $params = array()) + { + if (!isset($this->_items[$itemName])) { + return false; + } + $item = $this->_items[$itemName]; + Yii::trace('Checking permission: ' . $item->getName(), __METHOD__); + if (!isset($params['userId'])) { + $params['userId'] = $userId; + } + if ($this->executeBizRule($item->getBizRule(), $params, $item->getData())) { + if (in_array($itemName, $this->defaultRoles)) { + return true; + } + if (isset($this->_assignments[$userId][$itemName])) { + $assignment = $this->_assignments[$userId][$itemName]; + if ($this->executeBizRule($assignment->getBizRule(), $params, $assignment->getData())) { + return true; + } + } + foreach ($this->_children as $parentName => $children) { + if (isset($children[$itemName]) && $this->checkAccess($userId, $parentName, $params)) { + return true; + } + } + } + return false; + } + + /** + * Adds an item as a child of another item. + * @param string $itemName the parent item name + * @param string $childName the child item name + * @return boolean whether the item is added successfully + * @throws Exception if either parent or child doesn't exist or if a loop has been detected. + */ + public function addItemChild($itemName, $childName) + { + if (!isset($this->_items[$childName], $this->_items[$itemName])) { + throw new Exception("Either '$itemName' or '$childName' does not exist."); + } + $child = $this->_items[$childName]; + $item = $this->_items[$itemName]; + $this->checkItemChildType($item->getType(), $child->getType()); + if ($this->detectLoop($itemName, $childName)) { + throw new Exception("Cannot add '$childName' as a child of '$itemName'. A loop has been detected."); + } + if (isset($this->_children[$itemName][$childName])) { + throw new Exception("The item '$itemName' already has a child '$childName'."); + } + $this->_children[$itemName][$childName] = $this->_items[$childName]; + return true; + } + + /** + * Removes a child from its parent. + * Note, the child item is not deleted. Only the parent-child relationship is removed. + * @param string $itemName the parent item name + * @param string $childName the child item name + * @return boolean whether the removal is successful + */ + public function removeItemChild($itemName, $childName) + { + if (isset($this->_children[$itemName][$childName])) { + unset($this->_children[$itemName][$childName]); + return true; + } else { + return false; + } + } + + /** + * Returns a value indicating whether a child exists within a parent. + * @param string $itemName the parent item name + * @param string $childName the child item name + * @return boolean whether the child exists + */ + public function hasItemChild($itemName, $childName) + { + return isset($this->_children[$itemName][$childName]); + } + + /** + * Returns the children of the specified item. + * @param mixed $names the parent item name. This can be either a string or an array. + * The latter represents a list of item names. + * @return array all child items of the parent + */ + public function getItemChildren($names) + { + if (is_string($names)) { + return isset($this->_children[$names]) ? $this->_children[$names] : array(); + } + + $children = array(); + foreach ($names as $name) { + if (isset($this->_children[$name])) { + $children = array_merge($children, $this->_children[$name]); + } + } + return $children; + } + + /** + * Assigns an authorization item to a user. + * @param mixed $userId the user ID (see [[User::id]]) + * @param string $itemName the item name + * @param string $bizRule the business rule to be executed when [[checkAccess()]] is called + * for this particular authorization item. + * @param mixed $data additional data associated with this assignment + * @return Assignment the authorization assignment information. + * @throws Exception if the item does not exist or if the item has already been assigned to the user + */ + public function assign($userId, $itemName, $bizRule = null, $data = null) + { + if (!isset($this->_items[$itemName])) { + throw new Exception("Unknown authorization item '$itemName'."); + } elseif (isset($this->_assignments[$userId][$itemName])) { + throw new Exception("Authorization item '$itemName' has already been assigned to user '$userId'."); + } else { + return $this->_assignments[$userId][$itemName] = new Assignment($this, $itemName, $userId, $bizRule, $data); + } + } + + /** + * Revokes an authorization assignment from a user. + * @param mixed $userId the user ID (see [[User::id]]) + * @param string $itemName the item name + * @return boolean whether removal is successful + */ + public function revoke($userId, $itemName) + { + if (isset($this->_assignments[$userId][$itemName])) { + unset($this->_assignments[$userId][$itemName]); + return true; + } else { + return false; + } + } + + /** + * Returns a value indicating whether the item has been assigned to the user. + * @param mixed $userId the user ID (see [[User::id]]) + * @param string $itemName the item name + * @return boolean whether the item has been assigned to the user. + */ + public function isAssigned($userId, $itemName) + { + return isset($this->_assignments[$userId][$itemName]); + } + + /** + * Returns the item assignment information. + * @param mixed $userId the user ID (see [[User::id]]) + * @param string $itemName the item name + * @return Assignment the item assignment information. Null is returned if + * the item is not assigned to the user. + */ + public function getAssignment($userId, $itemName) + { + return isset($this->_assignments[$userId][$itemName]) ? $this->_assignments[$userId][$itemName] : null; + } + + /** + * Returns the item assignments for the specified user. + * @param mixed $userId the user ID (see [[User::id]]) + * @return array the item assignment information for the user. An empty array will be + * returned if there is no item assigned to the user. + */ + public function getAssignments($userId) + { + return isset($this->_assignments[$userId]) ? $this->_assignments[$userId] : array(); + } + + /** + * Returns the authorization items of the specific type and user. + * @param mixed $userId the user ID. Defaults to null, meaning returning all items even if + * they are not assigned to a user. + * @param integer $type the item type (0: operation, 1: task, 2: role). Defaults to null, + * meaning returning all items regardless of their type. + * @return array the authorization items of the specific type. + */ + public function getItems($userId = null, $type = null) + { + if ($type === null && $userId === null) { + return $this->_items; + } + $items = array(); + if ($userId === null) { + foreach ($this->_items as $name => $item) { + if ($item->getType() == $type) { + $items[$name] = $item; + } + } + } elseif (isset($this->_assignments[$userId])) { + foreach ($this->_assignments[$userId] as $assignment) { + $name = $assignment->getItemName(); + if (isset($this->_items[$name]) && ($type === null || $this->_items[$name]->getType() == $type)) { + $items[$name] = $this->_items[$name]; + } + } + } + return $items; + } + + /** + * Creates an authorization item. + * An authorization item represents an action permission (e.g. creating a post). + * It has three types: operation, task and role. + * Authorization items form a hierarchy. Higher level items inheirt permissions representing + * by lower level items. + * @param string $name the item name. This must be a unique identifier. + * @param integer $type the item type (0: operation, 1: task, 2: role). + * @param string $description description of the item + * @param string $bizRule business rule associated with the item. This is a piece of + * PHP code that will be executed when [[checkAccess()]] is called for the item. + * @param mixed $data additional data associated with the item. + * @return Item the authorization item + * @throws Exception if an item with the same name already exists + */ + public function createItem($name, $type, $description = '', $bizRule = null, $data = null) + { + if (isset($this->_items[$name])) { + throw new Exception('Unable to add an item whose name is the same as an existing item.'); + } + return $this->_items[$name] = new Item($this, $name, $type, $description, $bizRule, $data); + } + + /** + * Removes the specified authorization item. + * @param string $name the name of the item to be removed + * @return boolean whether the item exists in the storage and has been removed + */ + public function removeItem($name) + { + if (isset($this->_items[$name])) { + foreach ($this->_children as &$children) { + unset($children[$name]); + } + foreach ($this->_assignments as &$assignments) { + unset($assignments[$name]); + } + unset($this->_items[$name]); + return true; + } else { + return false; + } + } + + /** + * Returns the authorization item with the specified name. + * @param string $name the name of the item + * @return Item the authorization item. Null if the item cannot be found. + */ + public function getItem($name) + { + return isset($this->_items[$name]) ? $this->_items[$name] : null; + } + + /** + * Saves an authorization item to persistent storage. + * @param Item $item the item to be saved. + * @param string $oldName the old item name. If null, it means the item name is not changed. + * @throws Exception if an item with the same name already taken + */ + public function saveItem($item, $oldName = null) + { + if ($oldName !== null && ($newName = $item->getName()) !== $oldName) // name changed + { + if (isset($this->_items[$newName])) { + throw new Exception("Unable to change the item name. The name '$newName' is already used by another item."); + } + if (isset($this->_items[$oldName]) && $this->_items[$oldName] === $item) { + unset($this->_items[$oldName]); + $this->_items[$newName] = $item; + if (isset($this->_children[$oldName])) { + $this->_children[$newName] = $this->_children[$oldName]; + unset($this->_children[$oldName]); + } + foreach ($this->_children as &$children) { + if (isset($children[$oldName])) { + $children[$newName] = $children[$oldName]; + unset($children[$oldName]); + } + } + foreach ($this->_assignments as &$assignments) { + if (isset($assignments[$oldName])) { + $assignments[$newName] = $assignments[$oldName]; + unset($assignments[$oldName]); + } + } + } + } + } + + /** + * Saves the changes to an authorization assignment. + * @param Assignment $assignment the assignment that has been changed. + */ + public function saveAssignment($assignment) + { + } + + /** + * Saves authorization data into persistent storage. + * If any change is made to the authorization data, please make + * sure you call this method to save the changed data into persistent storage. + */ + public function save() + { + $items = array(); + foreach ($this->_items as $name => $item) { + $items[$name] = array( + 'type' => $item->getType(), + 'description' => $item->getDescription(), + 'bizRule' => $item->getBizRule(), + 'data' => $item->getData(), + ); + if (isset($this->_children[$name])) { + foreach ($this->_children[$name] as $child) { + $items[$name]['children'][] = $child->getName(); + } + } + } + + foreach ($this->_assignments as $userId => $assignments) { + foreach ($assignments as $name => $assignment) { + if (isset($items[$name])) { + $items[$name]['assignments'][$userId] = array( + 'bizRule' => $assignment->getBizRule(), + 'data' => $assignment->getData(), + ); + } + } + } + + $this->saveToFile($items, $this->authFile); + } + + /** + * Loads authorization data. + */ + public function load() + { + $this->clearAll(); + + $items = $this->loadFromFile($this->authFile); + + foreach ($items as $name => $item) { + $this->_items[$name] = new Item($this, $name, $item['type'], $item['description'], $item['bizRule'], $item['data']); + } + + foreach ($items as $name => $item) { + if (isset($item['children'])) { + foreach ($item['children'] as $childName) { + if (isset($this->_items[$childName])) { + $this->_children[$name][$childName] = $this->_items[$childName]; + } + } + } + if (isset($item['assignments'])) { + foreach ($item['assignments'] as $userId => $assignment) { + $this->_assignments[$userId][$name] = new Assignment($this, $name, $userId, $assignment['bizRule'], $assignment['data']); + } + } + } + } + + /** + * Removes all authorization data. + */ + public function clearAll() + { + $this->clearAssignments(); + $this->_children = array(); + $this->_items = array(); + } + + /** + * Removes all authorization assignments. + */ + public function clearAssignments() + { + $this->_assignments = array(); + } + + /** + * Checks whether there is a loop in the authorization item hierarchy. + * @param string $itemName parent item name + * @param string $childName the name of the child item that is to be added to the hierarchy + * @return boolean whether a loop exists + */ + protected function detectLoop($itemName, $childName) + { + if ($childName === $itemName) { + return true; + } + if (!isset($this->_children[$childName], $this->_items[$itemName])) { + return false; + } + foreach ($this->_children[$childName] as $child) { + if ($this->detectLoop($itemName, $child->getName())) { + return true; + } + } + return false; + } + + /** + * Loads the authorization data from a PHP script file. + * @param string $file the file path. + * @return array the authorization data + * @see saveToFile + */ + protected function loadFromFile($file) + { + if (is_file($file)) { + return require($file); + } else { + return array(); + } + } + + /** + * Saves the authorization data to a PHP script file. + * @param array $data the authorization data + * @param string $file the file path. + * @see loadFromFile + */ + protected function saveToFile($data, $file) + { + file_put_contents($file, " - * @author Alexander Kochetov - * @since 2.0 - */ -class AuthAssignment extends Object -{ - private $_auth; - private $_itemName; - private $_userId; - private $_bizRule; - private $_data; - - /** - * Constructor. - * @param IAuthManager $auth the authorization manager - * @param string $itemName authorization item name - * @param mixed $userId user ID (see [[User::id]]) - * @param string $bizRule the business rule associated with this assignment - * @param mixed $data additional data for this assignment - */ - public function __construct($auth, $itemName, $userId, $bizRule = null, $data = null) - { - $this->_auth = $auth; - $this->_itemName = $itemName; - $this->_userId = $userId; - $this->_bizRule = $bizRule; - $this->_data = $data; - } - - /** - * @return mixed user ID (see [[User::id]]) - */ - public function getUserId() - { - return $this->_userId; - } - - /** - * @return string the authorization item name - */ - public function getItemName() - { - return $this->_itemName; - } - - /** - * @return string the business rule associated with this assignment - */ - public function getBizRule() - { - return $this->_bizRule; - } - - /** - * @param string $value the business rule associated with this assignment - */ - public function setBizRule($value) - { - if ($this->_bizRule !== $value) { - $this->_bizRule = $value; - $this->_auth->saveAuthAssignment($this); - } - } - - /** - * @return mixed additional data for this assignment - */ - public function getData() - { - return $this->_data; - } - - /** - * @param mixed $value additional data for this assignment - */ - public function setData($value) - { - if ($this->_data !== $value) { - $this->_data = $value; - $this->_auth->saveAuthAssignment($this); - } - } -} diff --git a/framework/web/AuthItem.php b/framework/web/AuthItem.php deleted file mode 100644 index 0d3553d..0000000 --- a/framework/web/AuthItem.php +++ /dev/null @@ -1,261 +0,0 @@ - - * @author Alexander Kochetov - * @since 2.0 - */ -class AuthItem extends Object -{ - const TYPE_OPERATION = 0; - const TYPE_TASK = 1; - const TYPE_ROLE = 2; - - private $_auth; - private $_type; - private $_name; - private $_description; - private $_bizRule; - private $_data; - - /** - * Constructor. - * @param IAuthManager $auth authorization manager - * @param string $name authorization item name - * @param integer $type authorization item type. This can be 0 (operation), 1 (task) or 2 (role). - * @param string $description the description - * @param string $bizRule the business rule associated with this item - * @param mixed $data additional data for this item - */ - public function __construct($auth, $name, $type, $description = '', $bizRule = null, $data = null) - { - $this->_type = (int)$type; - $this->_auth = $auth; - $this->_name = $name; - $this->_description = $description; - $this->_bizRule = $bizRule; - $this->_data = $data; - } - - /** - * Checks to see if the specified item is within the hierarchy starting from this item. - * This method is expected to be internally used by the actual implementations - * of the [[IAuthManager::checkAccess()]]. - * @param string $itemName the name of the item to be checked - * @param array $params the parameters to be passed to business rule evaluation - * @return boolean whether the specified item is within the hierarchy starting from this item. - */ - public function checkAccess($itemName, $params = array()) - { - Yii::trace('Checking permission: ' . $this->_name, __METHOD__); - if ($this->_auth->executeBizRule($this->_bizRule, $params, $this->_data)) { - if ($this->_name == $itemName) { - return true; - } - foreach ($this->_auth->getItemChildren($this->_name) as $item) { - if ($item->checkAccess($itemName, $params)) { - return true; - } - } - } - return false; - } - - /** - * @return IAuthManager the authorization manager - */ - public function getAuthManager() - { - return $this->_auth; - } - - /** - * @return integer the authorization item type. This could be 0 (operation), 1 (task) or 2 (role). - */ - public function getType() - { - return $this->_type; - } - - /** - * @return string the item name - */ - public function getName() - { - return $this->_name; - } - - /** - * @param string $value the item name - */ - public function setName($value) - { - if ($this->_name !== $value) { - $oldName = $this->_name; - $this->_name = $value; - $this->_auth->saveAuthItem($this, $oldName); - } - } - - /** - * @return string the item description - */ - public function getDescription() - { - return $this->_description; - } - - /** - * @param string $value the item description - */ - public function setDescription($value) - { - if ($this->_description !== $value) { - $this->_description = $value; - $this->_auth->saveAuthItem($this); - } - } - - /** - * @return string the business rule associated with this item - */ - public function getBizRule() - { - return $this->_bizRule; - } - - /** - * @param string $value the business rule associated with this item - */ - public function setBizRule($value) - { - if ($this->_bizRule !== $value) { - $this->_bizRule = $value; - $this->_auth->saveAuthItem($this); - } - } - - /** - * @return mixed the additional data associated with this item - */ - public function getData() - { - return $this->_data; - } - - /** - * @param mixed $value the additional data associated with this item - */ - public function setData($value) - { - if ($this->_data !== $value) { - $this->_data = $value; - $this->_auth->saveAuthItem($this); - } - } - - /** - * Adds a child item. - * @param string $name the name of the child item - * @return boolean whether the item is added successfully - * @throws \yii\base\Exception if either parent or child doesn't exist or if a loop has been detected. - * @see IAuthManager::addItemChild - */ - public function addChild($name) - { - return $this->_auth->addItemChild($this->_name, $name); - } - - /** - * Removes a child item. - * Note, the child item is not deleted. Only the parent-child relationship is removed. - * @param string $name the child item name - * @return boolean whether the removal is successful - * @see IAuthManager::removeItemChild - */ - public function removeChild($name) - { - return $this->_auth->removeItemChild($this->_name, $name); - } - - /** - * Returns a value indicating whether a child exists - * @param string $name the child item name - * @return boolean whether the child exists - * @see IAuthManager::hasItemChild - */ - public function hasChild($name) - { - return $this->_auth->hasItemChild($this->_name, $name); - } - - /** - * Returns the children of this item. - * @return array all child items of this item. - * @see IAuthManager::getItemChildren - */ - public function getChildren() - { - return $this->_auth->getItemChildren($this->_name); - } - - /** - * Assigns this item to a user. - * @param mixed $userId the user ID (see [[User::id]]) - * @param string $bizRule the business rule to be executed when [[checkAccess()]] is called - * for this particular authorization item. - * @param mixed $data additional data associated with this assignment - * @return AuthAssignment the authorization assignment information. - * @throws \yii\base\Exception if the item has already been assigned to the user - * @see IAuthManager::assign - */ - public function assign($userId, $bizRule = null, $data = null) - { - return $this->_auth->assign($this->_name, $userId, $bizRule, $data); - } - - /** - * Revokes an authorization assignment from a user. - * @param mixed $userId the user ID (see [[User::id]]) - * @return boolean whether removal is successful - * @see IAuthManager::revoke - */ - public function revoke($userId) - { - return $this->_auth->revoke($this->_name, $userId); - } - - /** - * Returns a value indicating whether this item has been assigned to the user. - * @param mixed $userId the user ID (see [[User::id]]) - * @return boolean whether the item has been assigned to the user. - * @see IAuthManager::isAssigned - */ - public function isAssigned($userId) - { - return $this->_auth->isAssigned($this->_name, $userId); - } - - /** - * Returns the item assignment information. - * @param mixed $userId the user ID (see [[User::id]]) - * @return AuthAssignment the item assignment information. Null is returned if - * this item is not assigned to the user. - * @see IAuthManager::getAuthAssignment - */ - public function getAssignment($userId) - { - return $this->_auth->getAuthAssignment($this->_name, $userId); - } -} diff --git a/framework/web/AuthManager.php b/framework/web/AuthManager.php deleted file mode 100644 index 4888689..0000000 --- a/framework/web/AuthManager.php +++ /dev/null @@ -1,143 +0,0 @@ - - * @author Alexander Kochetov - * @since 2.0 - */ -abstract class AuthManager extends Component implements IAuthManager -{ - /** - * @var boolean Enable error reporting for bizRules. - */ - public $showErrors = false; - - /** - * @var array list of role names that are assigned to all users implicitly. - * These roles do not need to be explicitly assigned to any user. - * When calling [[checkAccess()]], these roles will be checked first. - * For performance reason, you should minimize the number of such roles. - * A typical usage of such roles is to define an 'authenticated' role and associate - * it with a biz rule which checks if the current user is authenticated. - * And then declare 'authenticated' in this property so that it can be applied to - * every authenticated user. - */ - public $defaultRoles = array(); - - /** - * Creates a role. - * This is a shortcut method to [[IAuthManager::createAuthItem()]]. - * @param string $name the item name - * @param string $description the item description. - * @param string $bizRule the business rule associated with this item - * @param mixed $data additional data to be passed when evaluating the business rule - * @return AuthItem the authorization item - */ - public function createRole($name, $description = '', $bizRule = null, $data = null) - { - return $this->createAuthItem($name, AuthItem::TYPE_ROLE, $description, $bizRule, $data); - } - - /** - * Creates a task. - * This is a shortcut method to [[IAuthManager::createAuthItem()]]. - * @param string $name the item name - * @param string $description the item description. - * @param string $bizRule the business rule associated with this item - * @param mixed $data additional data to be passed when evaluating the business rule - * @return AuthItem the authorization item - */ - public function createTask($name, $description = '', $bizRule = null, $data = null) - { - return $this->createAuthItem($name, AuthItem::TYPE_TASK, $description, $bizRule, $data); - } - - /** - * Creates an operation. - * This is a shortcut method to [[IAuthManager::createAuthItem()]]. - * @param string $name the item name - * @param string $description the item description. - * @param string $bizRule the business rule associated with this item - * @param mixed $data additional data to be passed when evaluating the business rule - * @return AuthItem the authorization item - */ - public function createOperation($name, $description = '', $bizRule = null, $data = null) - { - return $this->createAuthItem($name, AuthItem::TYPE_OPERATION, $description, $bizRule, $data); - } - - /** - * Returns roles. - * This is a shortcut method to [[IAuthManager::getAuthItems()]]. - * @param mixed $userId the user ID. If not null, only the roles directly assigned to the user - * will be returned. Otherwise, all roles will be returned. - * @return array roles (name=>AuthItem) - */ - public function getRoles($userId = null) - { - return $this->getAuthItems(AuthItem::TYPE_ROLE, $userId); - } - - /** - * Returns tasks. - * This is a shortcut method to [[IAuthManager::getAuthItems()]]. - * @param mixed $userId the user ID. If not null, only the tasks directly assigned to the user - * will be returned. Otherwise, all tasks will be returned. - * @return array tasks (name=>AuthItem) - */ - public function getTasks($userId = null) - { - return $this->getAuthItems(AuthItem::TYPE_TASK, $userId); - } - - /** - * Returns operations. - * This is a shortcut method to [[IAuthManager::getAuthItems()]]. - * @param mixed $userId the user ID. If not null, only the operations directly assigned to the user - * will be returned. Otherwise, all operations will be returned. - * @return array operations (name=>AuthItem) - */ - public function getOperations($userId = null) - { - return $this->getAuthItems(AuthItem::TYPE_OPERATION, $userId); - } - - /** - * Executes the specified business rule. - * @param string $bizRule the business rule to be executed. - * @param array $params parameters passed to [[IAuthManager::checkAccess()]]. - * @param mixed $data additional data associated with the authorization item or assignment. - * @return boolean whether the business rule returns true. - * If the business rule is empty, it will still return true. - */ - public function executeBizRule($bizRule, $params, $data) - { - return $bizRule === '' || $bizRule === null || ($this->showErrors ? eval($bizRule) != 0 : @eval($bizRule) != 0); - } - - /** - * Checks the item types to make sure a child can be added to a parent. - * @param integer $parentType parent item type - * @param integer $childType child item type - * @throws Exception if the item cannot be added as a child due to its incompatible type. - */ - protected function checkItemChildType($parentType, $childType) - { - static $types = array('operation', 'task', 'role'); - if ($parentType < $childType) { - throw new Exception("Cannot add an item of type '$types[$childType]' to an item of type '$types[$parentType]'."); - } - } -} diff --git a/framework/web/IAuthManager.php b/framework/web/IAuthManager.php deleted file mode 100644 index 1d2fbb9..0000000 --- a/framework/web/IAuthManager.php +++ /dev/null @@ -1,173 +0,0 @@ - - * @author Alexander Kochetov - * @since 2.0 - */ -interface IAuthManager -{ - /** - * Performs access check for the specified user. - * @param mixed $userId the user ID. This should be either an integer or a string representing - * the unique identifier of a user. See [[User::id]]. - * @param string $itemName the name of the operation that we are checking access to - * @param array $params name-value pairs that would be passed to biz rules associated - * with the tasks and roles assigned to the user. - * @return boolean whether the operations can be performed by the user. - */ - public function checkAccess($userId, $itemName, $params = array()); - - /** - * Creates an authorization item. - * An authorization item represents an action permission (e.g. creating a post). - * It has three types: operation, task and role. - * Authorization items form a hierarchy. Higher level items inheirt permissions representing - * by lower level items. - * @param string $name the item name. This must be a unique identifier. - * @param integer $type the item type (0: operation, 1: task, 2: role). - * @param string $description description of the item - * @param string $bizRule business rule associated with the item. This is a piece of - * PHP code that will be executed when [[checkAccess()]] is called for the item. - * @param mixed $data additional data associated with the item. - * @throws \yii\base\Exception if an item with the same name already exists - * @return AuthItem the authorization item - */ - public function createAuthItem($name, $type, $description = '', $bizRule = null, $data = null); - /** - * Removes the specified authorization item. - * @param string $name the name of the item to be removed - * @return boolean whether the item exists in the storage and has been removed - */ - public function removeAuthItem($name); - /** - * Returns the authorization items of the specific type and user. - * @param mixed $userId the user ID. Defaults to null, meaning returning all items even if - * they are not assigned to a user. - * @param integer $type the item type (0: operation, 1: task, 2: role). Defaults to null, - * meaning returning all items regardless of their type. - * @return array the authorization items of the specific type. - */ - public function getAuthItems($userId = null, $type = null); - /** - * Returns the authorization item with the specified name. - * @param string $name the name of the item - * @return AuthItem the authorization item. Null if the item cannot be found. - */ - public function getAuthItem($name); - /** - * Saves an authorization item to persistent storage. - * @param AuthItem $item the item to be saved. - * @param string $oldName the old item name. If null, it means the item name is not changed. - */ - public function saveAuthItem($item, $oldName = null); - - /** - * Adds an item as a child of another item. - * @param string $itemName the parent item name - * @param string $childName the child item name - * @throws \yii\base\Exception if either parent or child doesn't exist or if a loop has been detected. - */ - public function addItemChild($itemName, $childName); - /** - * Removes a child from its parent. - * Note, the child item is not deleted. Only the parent-child relationship is removed. - * @param string $itemName the parent item name - * @param string $childName the child item name - * @return boolean whether the removal is successful - */ - public function removeItemChild($itemName, $childName); - /** - * Returns a value indicating whether a child exists within a parent. - * @param string $itemName the parent item name - * @param string $childName the child item name - * @return boolean whether the child exists - */ - public function hasItemChild($itemName, $childName); - /** - * Returns the children of the specified item. - * @param mixed $itemName the parent item name. This can be either a string or an array. - * The latter represents a list of item names. - * @return array all child items of the parent - */ - public function getItemChildren($itemName); - - /** - * Assigns an authorization item to a user. - * @param mixed $userId the user ID (see [[User::id]]) - * @param string $itemName the item name - * @param string $bizRule the business rule to be executed when [[checkAccess()]] is called - * for this particular authorization item. - * @param mixed $data additional data associated with this assignment - * @return AuthAssignment the authorization assignment information. - * @throws \yii\base\Exception if the item does not exist or if the item has already been assigned to the user - */ - public function assign($userId, $itemName, $bizRule = null, $data = null); - /** - * Revokes an authorization assignment from a user. - * @param mixed $userId the user ID (see [[User::id]]) - * @param string $itemName the item name - * @return boolean whether removal is successful - */ - public function revoke($userId, $itemName); - /** - * Returns a value indicating whether the item has been assigned to the user. - * @param mixed $userId the user ID (see [[User::id]]) - * @param string $itemName the item name - * @return boolean whether the item has been assigned to the user. - */ - public function isAssigned($userId, $itemName); - /** - * Returns the item assignment information. - * @param mixed $userId the user ID (see [[User::id]]) - * @param string $itemName the item name - * @return AuthAssignment the item assignment information. Null is returned if - * the item is not assigned to the user. - */ - public function getAuthAssignment($userId, $itemName); - /** - * Returns the item assignments for the specified user. - * @param mixed $userId the user ID (see [[User::id]]) - * @return array the item assignment information for the user. An empty array will be - * returned if there is no item assigned to the user. - */ - public function getAuthAssignments($userId); - /** - * Saves the changes to an authorization assignment. - * @param AuthAssignment $assignment the assignment that has been changed. - */ - public function saveAuthAssignment($assignment); - /** - * Removes all authorization data. - */ - public function clearAll(); - /** - * Removes all authorization assignments. - */ - public function clearAuthAssignments(); - /** - * Saves authorization data into persistent storage. - * If any change is made to the authorization data, please make - * sure you call this method to save the changed data into persistent storage. - */ - public function save(); - /** - * Executes a business rule. - * A business rule is a piece of PHP code that will be executed when [[checkAccess()]] is called. - * @param string $bizRule the business rule to be executed. - * @param array $params additional parameters to be passed to the business rule when being executed. - * @param mixed $data additional data that is associated with the corresponding authorization item or assignment - * @return boolean whether the execution returns a true value. - * If the business rule is empty, it will also return true. - */ - public function executeBizRule($bizRule, $params, $data); -}