* @since 2.0 */ class DbManager extends Component implements ManagerInterface { /** * @var Connection|array|string the DB connection object or the application component ID of the DB connection. * After the DbManager object is created, if you want to change this property, you should only assign it * with a DB connection object. * Starting from version 2.0.2, this can also be a configuration array for creating the object. */ public $db = 'db'; /** * @var string the name of the table storing authorization roles. Defaults to "auth_role". */ public $roleTable = '{{%auth_role}}'; /** * @var string the name of the table storing authorization role assignments. Defaults to "auth_assignment". */ public $assignmentTable = '{{%auth_users_roles}}'; /** * @var string the name of the table storing authorization roles. Defaults to "auth_role". */ public $permTable = '{{%auth_perm}}'; /** * @var Cache|array|string the cache used to improve RBAC performance. This can be one of the following: * * - an application component ID (e.g. `cache`) * - a configuration array * - a [[\yii\caching\Cache]] object * * When this is not set, it means caching is not enabled. * * Note that by enabling RBAC cache, all auth roles, rules and auth role parent-child relationships will * be cached and loaded into memory. This will improve the performance of RBAC perm check. However, * it does require extra memory and as a result may not be appropriate if your RBAC system contains too many * auth roles. You should seek other RBAC implementations (e.g. RBAC based on Redis storage) in this case. * * Also note that if you modify RBAC roles, rules or parent-child relationships from outside of this component, * you have to manually call [[invalidateCache()]] to ensure data consistency. * * @since 2.0.3 */ public $cache; /** * @var string the key used to store RBAC data in cache * @see cache * @since 2.0.3 */ public $cacheKey = 'rbac'; /** * @var Roles[] all auth roles (role_id => Role) */ protected $roles; /** * @var Perm[] all auth rules (perm_id => Perm) */ protected $perms; const SUPPER_ADMIN_NAME = '超级管理员'; /** * Initializes the application component. * This method overrides the parent implementation by establishing the database connection. */ public function init() { parent::init(); $this->db = Instance::ensure($this->db, Connection::className()); if ($this->cache !== null) { $this->cache = Instance::ensure($this->cache, Cache::className()); } } /** * @inheritdoc */ public function checkAccess($userId, $permId, $params = []) { $perms = $this->getPermsByUser($userId); $this->loadFromCache(); return $this->checkAccessFromCache($permId, $perms, $params); } /** * Performs access check for the specified user based on the data loaded from cache. */ protected function checkAccessFromCache($permId, $perms, $params = []) { if (!isset($this->perms[$permId])) { return false; } $perm = $this->perms[$permId]; Yii::trace("Checking permission: $perm->name", __METHOD__); foreach ($perms as $p) { if ($p->permId == $permId) { return true; } } return false; } public function invalidateCache() { if ($this->cache !== null) { $this->cache->delete($this->cacheKey); $this->roles = null; $this->perms = null; } } /** * @inheritdoc */ public function createRole($name) { $role = new Role; $role->name = $name; return $role; } /** * @inheritdoc */ public function addRole($role) { $row = (new Query)->from($this->roleTable) ->where(['name' => $role->name]) ->one($this->db); if ($row) { return false; } $this->db->createCommand() ->insert($this->roleTable, [ 'name' => $role->name, 'description' => $role->description, ])->execute(); $this->invalidateCache(); return true; } /** * @inheritdoc */ public function removeRole($role) { $this->db->createCommand() ->delete($this->assignmentTable, ['role_id' => $role->roleId]) ->execute(); $this->db->createCommand() ->delete($this->roleTable, ['role_id' => $role->roleId]) ->execute(); $this->invalidateCache(); return true; } /** * @inheritdoc */ public function updateRole($roleId, $role) { $this->db->createCommand() ->update($this->roleTable, [ 'name' => $role->name, 'description' => $role->description, ], [ 'role_id' => $roleId, ])->execute(); $this->invalidateCache(); return true; } /** * @inheritdoc */ public function setRolePerms($roleId, $perms = '') { $this->db->createCommand() ->update($this->roleTable, [ 'perms' => $perms, ], [ 'role_id' => $roleId, ])->execute(); $this->invalidateCache(); return true; } /** * @inheritdoc */ public function getRole($roleId) { if (empty($roleId)) { return null; } if (!empty($this->roles[$roleId])) { return $this->roles[$roleId]; } $row = (new Query)->from($this->roleTable) ->where(['role_id' => $roleId]) ->one($this->db); if ($row === false) { return null; } return $this->populateRole($row); } /** * @inheritdoc */ public function getRoles() { $query = (new Query) ->from($this->roleTable); $roles = []; foreach ($query->all($this->db) as $row) { $roles[$row['role_id']] = $this->populateRole($row); } return $roles; } /** * @inheritdoc */ public function getRolesByUser($userId) { if (empty($userId)) { return []; } $query = (new Query)->select('b.*') ->from(['a' => $this->assignmentTable, 'b' => $this->roleTable]) ->where('{{a}}.[[role_id]]={{b}}.[[role_id]]') ->andWhere(['a.sys_user_id' => (int) $userId]); $roles = []; foreach ($query->all($this->db) as $row) { $roles[$row['role_id']] = $this->populateRole($row); } return $roles; } /** * Populates an auth role with the data fetched from database * @param array $row the data from the auth role table * @return Role the populated auth role instance (either Role) */ protected function populateRole($row) { $class = Role::className(); return new $class([ 'roleId' => $row['role_id'], 'name' => $row['name'], 'perms' => $row['perms'], 'description' => $row['description'], ]); } /** * @inheritdoc */ public function getPerm($permId) { if (empty($permId)) { return null; } if (!empty($this->perms[$permId])) { return $this->perms[$permId]; } $row = (new Query)->from($this->permTable) ->where(['perm_id' => $permId]) ->one($this->db); if ($row === false) { return null; } return $this->populatePerm($row); } /** * @inheritdoc */ public function getPerms() { $query = (new Query) ->from($this->permTable); $perms = []; foreach ($query->all($this->db) as $row) { $perms[$row['perm_id']] = $this->populatePerm($row); } return $perms; } /** * @inheritdoc */ public function getPermsByRole($roleId) { $perms = []; $role = $this->getRole($roleId); if ($role->perms && $permIds = explode(',', $role->perms)) { foreach ($permIds as $permId) { if ($this->getPerm($permId)) { $perms[$permId] = $this->getPerm($permId); } } } return $perms; } /** * @inheritdoc */ public function getPermsByUser($userId) { $perms = []; $assignments = $this->getAssignments($userId); foreach ($assignments as $roleId => $role) { $perms = array_merge($perms, $this->getPermsByRole($roleId)); } return $perms; } /** * Populates an auth perm with the data fetched from database * @param array $row the data from the auth role table * @return Role the populated auth role instance (either Role) */ protected function populatePerm($row) { $class = Perm::className(); return new $class([ 'permId' => $row['perm_id'], 'name' => $row['name'], ]); } /** * @inheritdoc */ public function assign($role, $userId) { $assignment = new Assignment([ 'userId' => $userId, 'roleId' => $role->roleId, ]); $this->db->createCommand() ->insert($this->assignmentTable, [ 'sys_user_id' => $assignment->userId, 'role_id' => $assignment->roleId, ])->execute(); return $assignment; } /** * @inheritdoc */ public function getAssignment($roleId, $userId) { if (empty($userId)) { return null; } $row = (new Query)->from($this->assignmentTable) ->where(['user_id' => (int) $userId, 'roleId' => $roleId]) ->one($this->db); if ($row === false) { return null; } return new Assignment([ 'userId' => $row['user_id'], 'roleId' => $row['role_id'], ]); } /** * @inheritdoc */ public function getAssignments($userId) { if (empty($userId)) { return []; } $query = (new Query) ->from($this->assignmentTable) ->where(['sys_user_id' => (int) $userId]); $assignments = []; foreach ($query->all($this->db) as $row) { $assignments[$row['role_id']] = new Assignment([ 'userId' => $row['sys_user_id'], 'roleId' => $row['role_id'], ]); } return $assignments; } /** * @inheritdoc */ public function revoke($role, $userId) { if (empty($userId)) { return false; } return $this->db->createCommand() ->delete($this->assignmentTable, ['sys_user_id' => (int) $userId, 'role_id' => $role->role_id]) ->execute() > 0; } /** * @inheritdoc */ public function revokeAll($userId) { return $this->db->createCommand() ->delete($this->assignmentTable, ['sys_user_id' => (int) $userId]) ->execute() > 0; } /** * @inheritdoc */ public function removeAllPerms() { } /** * @inheritdoc */ public function removeAllRoles() { } public function loadFromCache() { if ($this->roles !== null || !$this->cache instanceof Cache) { return; } $data = $this->cache->get($this->cacheKey); if (is_array($data) && isset($data[0], $data[1])) { list ($this->roles, $this->perms) = $data; return; } $query = (new Query)->from($this->roleTable); $this->roles = []; foreach ($query->all($this->db) as $row) { $this->roles[$row['role_id']] = $this->populateRole($row); } $query = (new Query)->from($this->permTable); $this->perms = []; foreach ($query->all($this->db) as $row) { $this->perms[$row['perm_id']] = $this->populatePerm($row);; } $this->cache->set($this->cacheKey, [$this->roles, $this->perms]); } /** * Returns all role assignment information for the specified role. * @param string $roleId * @return Assignment[] the assignments. An empty array will be * returned if role is not assigned to any user. * @since 2.0.7 */ public function getUserIdsByRole($roleId) { if (empty($roleId)) { return []; } return (new Query)->select('[[sys_user_id]]') ->from($this->assignmentTable) ->where(['role_id' => $roleId])->column($this->db); } /** * @param $roleName * @return Role|null */ public function getRoleByName($roleName) { if (empty($roleName)) { return null; } $row = (new Query)->from($this->roleTable) ->where(['name' => $roleName]) ->one($this->db); if ($row === false) { return null; } return $this->populateRole($row); } }