| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710 | <?phpnamespace common\modules\rbac\components;use Yii;use yii\caching\Cache;use yii\caching\TagDependency;use yii\db\Connection;use yii\db\Query;use yii\di\Instance;use yii\rbac\Assignment;use yii\rbac\Item;use yii\rbac\Permission;use yii\rbac\Role;use yii\rbac\Rule;/** * DbManager represents an authorization manager that stores authorization information in database. * * The database connection is specified by [[$db]]. The database schema could be initialized by applying migration: * * ~~~ * yii migrate --migrationPath=@yii/rbac/migrations/ * ~~~ * * If you don't want to use migration and need SQL instead, files for all databases are in migrations directory. * * You may change the names of the three tables used to store the authorization data by setting [[\yii\rbac\DbManager::$itemTable]], * [[\yii\rbac\DbManager::$itemChildTable]] and [[\yii\rbac\DbManager::$assignmentTable]]. * * @author Misbahul D Munir <misbahuldmunir@gmail.com> * * @since 1.0 */class DbManager extends \yii\rbac\DbManager{    const PART_ITEMS = 'mdm.admin.items';    const PART_CHILDREN = 'mdm.admin.children';    const PART_RULES = 'mdm.admin.rules';    /**     * @var bool Enable caching     */    public $enableCaching = false;    /**     * @var string|Cache Cache component     */    public $cache = 'cache';    /**     * @var int Cache duration     */    public $cacheDuration = 0;    /**     * @var Item[]     *             itemName => item     */    private $_items;    /**     * @var array     *            itemName => childName[]     */    private $_children;    /**     * @var array     *            userId => itemName[]     */    private $_assignments = [];    /**     * @var Rule[]     *             ruleName => rule     */    private $_rules;    /**     * {@inheritdoc}     */    public function init()    {        parent::init();        $this->db = Instance::ensure($this->db, Connection::className());        if ($this->enableCaching) {            $this->cache = Instance::ensure($this->cache, Cache::className());        } else {            $this->cache = null;        }    }    /**     * {@inheritdoc}     */    public function checkAccess($userId, $permissionName, $params = [])    {        $this->loadItems();        $this->loadChildren();        $this->loadRules();        $assignments = $this->getAssignments($userId);        return $this->checkAccessRecursive($userId, $permissionName, $params, $assignments);    }    /**     * {@inheritdoc}     */    public function getAssignments($userId)    {        $this->loadAssignments($userId);        return isset($this->_assignments[$userId]) ? $this->_assignments[$userId] : [];    }    /**     * {@inheritdoc}     */    protected function checkAccessRecursive($user, $itemName, $params, $assignments)    {        if (!isset($this->_items[$itemName])) {            return false;        }        /** @var Item $item */        $item = $this->_items[$itemName];        Yii::trace($item instanceof Role ? "Checking role: $itemName" : "Checking permission : $itemName", __METHOD__);        if (!$this->executeRule($user, $item, $params)) {            return false;        }        if (isset($assignments[$itemName]) || in_array($itemName, $this->defaultRoles)) {            return true;        }        foreach ($this->_children as $parentName => $children) {            if (in_array($itemName, $children) && $this->checkAccessRecursive($user, $parentName, $params, $assignments)) {                return true;            }        }        return false;    }    /**     * {@inheritdoc}     */    public function addChild($parent, $child)    {        $this->loadItems();        $this->loadChildren();        parent::addChild($parent, $child);        $this->_children[$parent->name][] = $child->name;        $this->invalidate(self::PART_CHILDREN);        return true;    }    /**     * {@inheritdoc}     */    public function removeChild($parent, $child)    {        $result = parent::removeChild($parent, $child);        if ($this->_children !== null) {            $query = (new Query())                ->select('child')                ->from($this->itemChildTable)                ->where(['parent' => $parent->name]);            $this->_children[$parent->name] = $query->column($this->db);        }        $this->invalidate(self::PART_CHILDREN);        return $result;    }    /**     * {@inheritdoc}     */    public function hasChild($parent, $child)    {        $this->loadChildren();        return isset($this->_children[$parent->name]) && in_array($child->name, $this->_children[$parent->name]);    }    /**     * {@inheritdoc}     */    public function assign($role, $userId)    {        $assignment = parent::assign($role, $userId);        if (isset($this->_assignments[$userId])) {            $this->_assignments[$userId][$role->name] = $assignment;        }        return $assignment;    }    /**     * {@inheritdoc}     */    public function revoke($role, $userId)    {        $result = parent::revoke($role, $userId);        unset($this->_assignments[$userId]);        return $result;    }    /**     * {@inheritdoc}     */    public function revokeAll($userId)    {        if (empty($userId)) {            return false;        }        $result = parent::revokeAll($userId);        $this->_assignments[$userId] = [];        return $result;    }    /**     * {@inheritdoc}     */    public function getAssignment($roleName, $userId)    {        $this->loadItems();        $this->loadAssignments($userId);        if (isset($this->_assignments[$userId][$roleName], $this->_items[$roleName])) {            return $this->_items[$roleName];        }        return;    }    /**     * {@inheritdoc}     */    protected function getItems($type)    {        $this->loadItems();        $items = [];        foreach ($this->_items as $name => $item) {            /** @var Item $item */            if ($item->type == $type) {                $items[$name] = $item;            }        }        return $items;    }    /**     * {@inheritdoc}     */    public function removeItem($item)    {        parent::removeItem($item);        $this->_assignments = [];        $this->_children = $this->_items = null;        $this->invalidate([self::PART_ITEMS, self::PART_CHILDREN]);        return true;    }    /**     * {@inheritdoc}     */    public function getItem($name)    {        $this->loadItems();        return isset($this->_items[$name]) ? $this->_items[$name] : null;    }    /**     * {@inheritdoc}     */    public function updateRule($name, $rule)    {        parent::updateRule($name, $rule);        if ($rule->name !== $name) {            $this->_items = null;            $this->invalidate(self::PART_ITEMS);        }        if ($this->_rules !== null) {            unset($this->_rules[$name]);            $this->_rules[$rule->name] = $rule;        }        $this->invalidate(self::PART_RULES);        return true;    }    /**     * {@inheritdoc}     */    public function getRule($name)    {        $this->loadRules();        return isset($this->_rules[$name]) ? $this->_rules[$name] : null;    }    /**     * {@inheritdoc}     */    public function getRules()    {        $this->loadRules();        return $this->_rules;    }    /**     * {@inheritdoc}     */    public function getRolesByUser($userId)    {        $this->loadItems();        $roles = [];        foreach ($this->getAssignments($userId) as $name => $asgn) {            if (isset($this->_items[$name]) && $this->_items[$name]->type == Item::TYPE_ROLE) {                $roles[$name] = $this->_items[$name];            }        }        return $roles;    }    /**     * {@inheritdoc}     */    public function getPermissionsByRole($roleName)    {        $childrenList = $this->getChildrenList();        $result = [];        $this->getChildrenRecursive($roleName, $childrenList, $result);        if (empty($result)) {            return [];        }        $this->loadItems();        $permissions = [];        foreach (array_keys($result) as $itemName) {            if (isset($this->_items[$itemName]) && $this->_items[$itemName] instanceof Permission) {                $permissions[$itemName] = $this->_items[$itemName];            }        }        return $permissions;    }    /**     * {@inheritdoc}     */    protected function getChildrenList()    {        $this->loadChildren();        return $this->_children;    }    /**     * {@inheritdoc}     */    public function getPermissionsByUser($userId)    {        $childrenList = $this->getChildrenList();        $result = [];        foreach ($this->getAssignments($userId) as $roleName => $asgn) {            $this->getChildrenRecursive($roleName, $childrenList, $result);        }        if (empty($result)) {            return [];        }        $this->loadItems();        $permissions = [];        foreach (array_keys($result) as $itemName) {            if (isset($this->_items[$itemName]) && $this->_items[$itemName] instanceof Permission) {                $permissions[$itemName] = $this->_items[$itemName];            }        }        return $permissions;    }    /**     * {@inheritdoc}     */    public function getChildren($name)    {        $this->loadItems();        $this->loadChildren();        $items = [];        if (isset($this->_children[$name])) {            foreach ($this->_children[$name] as $itemName) {                $items[$itemName] = $this->_items[$itemName];            }        }        return $items;    }    /**     * {@inheritdoc}     */    public function removeAll()    {        $this->_children = [];        $this->_items = [];        $this->_assignments = [];        $this->_rules = [];        $this->removeAllAssignments();        $this->db->createCommand()->delete($this->itemChildTable)->execute();        $this->db->createCommand()->delete($this->itemTable)->execute();        $this->db->createCommand()->delete($this->ruleTable)->execute();        $this->invalidate([self::PART_ITEMS, self::PART_CHILDREN, self::PART_RULES]);    }    /**     * {@inheritdoc}     */    protected function removeAllItems($type)    {        parent::removeAllItems($type);        $this->_assignments = [];        $this->_children = $this->_items = null;        $this->invalidate([self::PART_ITEMS, self::PART_CHILDREN]);    }    /**     * {@inheritdoc}     */    public function removeAllRules()    {        parent::removeAllRules();        $this->_rules = [];        $this->_items = null;        $this->invalidate([self::PART_ITEMS, self::PART_RULES]);    }    /**     * {@inheritdoc}     */    public function removeAllAssignments()    {        parent::removeAllAssignments();        $this->_assignments = [];    }    /**     * {@inheritdoc}     */    protected function removeRule($rule)    {        parent::removeRule($rule);        if ($this->_rules !== null) {            unset($this->_rules[$rule->name]);        }        $this->_items = null;        $this->invalidate([self::PART_ITEMS, self::PART_RULES]);        return true;    }    /**     * {@inheritdoc}     */    protected function addRule($rule)    {        parent::addRule($rule);        if ($this->_rules !== null) {            $this->_rules[$rule->name] = $rule;        }        $this->invalidate(self::PART_RULES);        return true;    }    /**     * {@inheritdoc}     */    protected function updateItem($name, $item)    {        parent::updateItem($name, $item);        if ($item->name !== $name) {            $this->_assignments = [];            $this->_children = null;            $this->invalidate(self::PART_CHILDREN);        }        $this->_items = null;        $this->invalidate(self::PART_RULES);        return true;    }    /**     * {@inheritdoc}     */    protected function addItem($item)    {        parent::addItem($item);        if ($this->_items !== null) {            $this->_items[$item->name] = $item;        }        $this->invalidate(self::PART_ITEMS);        return true;    }    /**     * Invalidate cache.     *     * @param string $parts     */    private function invalidate($parts)    {        if ($this->enableCaching) {            TagDependency::invalidate($this->cache, $parts);        }    }    /**     * Build key cache.     *     * @param string $part     *     * @return mixed     */    private function buildKey($part)    {        return [__CLASS__, $part];    }    /**     * Get data from cache.     *     * @param string $part     *     * @return mixed     */    private function getFromCache($part)    {        if ($this->enableCaching) {            return $this->cache->get($this->buildKey($part));        }        return false;    }    /**     * Save data to cache.     *     * @param string $part     * @param mixed  $data     */    private function saveToCache($part, $data)    {        if ($this->enableCaching) {            $this->cache->set($this->buildKey($part), $data, $this->cacheDuration, new TagDependency([                'tags' => $part,            ]));        }    }    /**     * Load data. If avaliable in memory, get from memory     * If no, get from cache. If no avaliable, get from database.     */    private function loadItems()    {        $part = self::PART_ITEMS;        if ($this->_items === null && ($this->_items = $this->getFromCache($part)) === false) {            $query = (new Query())->from($this->itemTable);            $this->_items = [];            foreach ($query->all($this->db) as $row) {                $this->_items[$row['name']] = $this->populateItem($row);            }            $this->saveToCache($part, $this->_items);        }    }    /**     * Load data. If avaliable in memory, get from memory     * If no, get from cache. If no avaliable, get from database.     */    private function loadChildren()    {        $this->loadItems();        $part = self::PART_CHILDREN;        if ($this->_children === null && ($this->_children = $this->getFromCache($part)) === false) {            $query = (new Query())->from($this->itemChildTable);            $this->_children = [];            foreach ($query->all($this->db) as $row) {                if (isset($this->_items[$row['parent']], $this->_items[$row['child']])) {                    $this->_children[$row['parent']][] = $row['child'];                }            }            $this->saveToCache($part, $this->_children);        }    }    /**     * Load data. If avaliable in memory, get from memory     * If no, get from cache. If no avaliable, get from database.     */    private function loadRules()    {        $part = self::PART_RULES;        if ($this->_rules === null && ($this->_rules = $this->getFromCache($part)) === false) {            $query = (new Query())->from($this->ruleTable);            $this->_rules = [];            foreach ($query->all($this->db) as $row) {                $rule = @unserialize($row['data']);                if ($rule instanceof Rule) {                    $this->_rules[$row['name']] = $rule;                }            }            $this->saveToCache($part, $this->_rules);        }    }    /**     * Load data. If avaliable in memory, get from memory     * If no, get from cache. If no avaliable, get from database.     */    private function loadAssignments($userId)    {        if (!isset($this->_assignments[$userId]) && !empty($userId)) {            $query = (new Query())                ->from($this->assignmentTable)                ->where(['user_id' => (string) $userId]);            $this->_assignments[$userId] = [];            foreach ($query->all($this->db) as $row) {                $this->_assignments[$userId][$row['item_name']] = new Assignment([                    'userId' => $row['user_id'],                    'roleName' => $row['item_name'],                    'createdAt' => $row['created_at'],                ]);            }        }    }    /**     * {@inheritdoc}     */    public function removeChildren($parent)    {        $result = parent::removeChildren($parent);        if ($this->_children !== null) {            unset($this->_children[$parent->name]);        }        $this->invalidate(self::PART_CHILDREN);        return $result;    }    /**     * Returns both roles and permissions assigned to user.     *     * @param  integer $userId     * @return array     */    public function getItemsByUser($userId)    {        if (empty($userId)) {            return [];        }        $query = (new Query)->select('b.*')            ->from(['a' => $this->assignmentTable, 'b' => $this->itemTable])            ->where('{{a}}.[[item_name]]={{b}}.[[name]]')            ->andWhere(['a.user_id' => (string) $userId]);        $roles = [];        foreach ($query->all($this->db) as $row) {            $roles[$row['name']] = $this->populateItem($row);        }        return $roles;    }}
 |