Yii2 RBAC基于组的每个用户的多个分配

时间:2015-04-01 02:19:53

标签: mysql yii2 rbac

我的应用程序在技术上有两个区域,全局区域(反馈,用户配置文件,用户设置等)和组区域(联系人,项目,组配置文件,组设置等)。

我正在使用RBAC DBManager作为全局区域,它工作得很好,但我遇到了为组区域实现授权机制的问题。

原因是组可以在用户之间共享,并且用户可能在group_access表(id,group_id,user_id,item_name)中有多个分配,因为它们可能是多个组的成员,并且它们可能有不同这些组的权限级别。

这是我的身份验证设置:

$auth = Yii::$app->authManager;

    // group permissions
    $manageGroupUsers = $auth->createPermission('manage_group_users');
    $manageGroupUsers->description = 'Manage Group Users';
    $auth->add($manageGroupUsers);

    $manageGroupSettings = $auth->createPermission('manage_group_settings');
    $manageGroupSettings->description = 'Manage Group Settings';
    $auth->add($manageGroupSettings);

    // app permissions
    $manageAppUsers = $auth->createPermission('manage_app_users');
    $manageAppUsers->description = 'Manage App Users';
    $auth->add($manageAppUsers);

    $manageAppGroups = $auth->createPermission('manage_app_groups');
    $manageAppGroups->description = 'Manage App Groups';
    $auth->add($manageAppGroups);

    $manageAppSettings = $auth->createPermission('manage_app_settings');
    $manageAppSettings->description = 'Manage App Settings';
    $auth->add($manageAppSettings);

    $manageAppFeedback = $auth->createPermission('manage_app_feedback');
    $manageAppFeedback->description = 'Manage App Feedback';
    $auth->add($manageAppFeedback);

    // group roles
    // -- create role
    $groupUser = $auth->createRole('group_user');
    $groupUser->description = 'Group Users';
    $auth->add($groupUser);

    // -- create role
    $groupAdmin = $auth->createRole('group_admin');
    $groupAdmin->description = 'Group Administrators';
    $auth->add($groupAdmin);
    // add permissions
    $auth->addChild($groupAdmin, $manageGroupUsers);
    $auth->addChild($groupAdmin, $manageGroupSettings);
    // inherit permissions
    $auth->addChild($groupAdmin, $groupUser);

    // -- create role
    $groupCreator = $auth->createRole('group_creator');
    $groupCreator->description = 'Group Creators';
    $auth->add($groupCreator);
    // inherit permissions
    $auth->addChild($groupCreator, $groupAdmin);

    // app roles
    // -- create role
    $appUser = $auth->createRole('app_user');
    $appUser->description = 'App Users';
    $auth->add($appUser);

    // -- create role
    $appSupport = $auth->createRole('app_support');
    $appSupport->description = 'Support Users';
    $auth->add($appSupport);
    // add permissions
    $auth->addChild($appSupport, $manageAppFeedback);

    // -- create role
    $appAdmin = $auth->createRole('app_admin');
    $appAdmin->description = 'App Administrators';
    $auth->add($appAdmin);
    // add permissions
    $auth->addChild($appAdmin, $manageAppUsers);
    $auth->addChild($appAdmin, $manageAppGroups);
    $auth->addChild($appAdmin, $manageAppSettings);
    // inherit permissions
    $auth->addChild($appAdmin, $appUser);
    $auth->addChild($appAdmin, $appSupport);

    // -- create role
    $appCreator = $auth->createRole('app_creator');
    $appCreator->description = 'App Creators';
    $auth->add($appCreator);
    // inherit permissions
    $auth->addChild($appCreator, $appAdmin);

我的group_access表与auth_assignment表具有相同的模式,但它有一个group_id列,而user_id列不是唯一的。

用户将只有一个关于全局区域的作业,但可能在组区域有许多不同的分配,因为它们可能在组a上具有管理主动权,但仅在组b上具有用户权限。

我的数据库设置如下:

  1. 用户(status_id,用户名,auth_key,password_hash,电子邮件等)

  2. 组(status_id,名称,描述等)

  3. Group_Access(group_id,user_id,item_name)每个用户为他们有权访问的每个组获取一个分配。

    sample_group_access_records [     [     'id'=> 1,     'user_id'=> 35,     'group_id'=> 17,     'item_name'=> 'group_admin'     ]     [     'id'=> 2,     'user_id'=> 35,     'group_id'=> 356,     'item_name'=> 'GROUP_USER'     ]     [     'id'=> 3,     'user_id'=> 35,     'group_id'=> 211,     'item_name'=> 'group_creator'     ] ];

  4. checkAccess函数可以限定userID,我甚至可以使用较短的“can”版本,这对于登录用户非常有用,但我需要根据用户选项检查访问权限,如下所示:

    Option::getOption('user', 'active_group_id')
    

    这是一个自定义函数,用于从用户选项表中提取活动组ID。如果用户切换组,则会更改。我的期权模型有三种类型'app','user','group'。

    如果我能找出一个与原生checkAccess相同的功能但是可以调用checkGroupAccess并自动获取active_group_id并从group_access表中提取用户分配并执行权限检查,那将是很好的。

    我希望这是有道理的。

    感谢您的时间。

    麦克

    **更新**

    所以,我有一个解决方案,它使用自定义checkAccess函数来检查组或全局区域的适当权限。

    我有两个表(user_access,group_access),它们具有与默认{{auth_assignment}}表类似的模式,我现在不使用它。我正在使用{{auth_item}},{{auth_item_child}}和{{auth_rule}}表。

    我有两个模型,每个访问表一个GroupAccess => group_access和UserAccess => user_access。

    我还有一个访问函数模型,并已将其映射到组件配置。

    这是我的访问模式:

    <?php
    
    namespace app\models;
    
    use Yii;
    
    class Access
    {
    
    public function canUser($type, $permissionName, $params = [])
    {
    
        switch ($type) {
    
            case 'group':
    
            $userID = Yii::$app->user->identity->id;
            $groupID = Yii::$app->options->getOption('user', 'active_group_id');
    
            $queryAll = GroupAccess::find()
            ->where('user_id = :user_id and group_id = :group_id', [':user_id' => $userID, ':group_id' => $groupID])
            ->asArray()
            ->all();
    
            $assignments = [];
            foreach ($queryAll as $queryItem) {
                $assignments[$queryItem['item_name']] = [
                'userId' => $queryItem['user_id'],
                'roleName' => $queryItem['item_name'],
                'createdAt' => $queryItem['created_date'],
                ];
            }
    
            $result = self::checkAccess($userID, $permissionName, $assignments, $params);
    
            return $result;
    
            break;
    
            case 'user':
    
            $userID = Yii::$app->user->identity->id;
    
            $queryAll = UserAccess::find()
            ->where(['user_id' => $userID])
            ->asArray()
            ->all();
    
            $assignments = [];
            foreach ($queryAll as $queryItem) {
                $assignments[$queryItem['item_name']] = [
                'userId' => $queryItem['user_id'],
                'roleName' => $queryItem['item_name'],
                'createdAt' => $queryItem['created_date'],
                ];
            }
    
            $result = self::checkAccess($userID, $permissionName, $assignments, $params);
    
            return $result;
    
            break;
    
        }
    
    }
    
    public function checkAccess($userID, $permissionName, $assignments, $params = [])
    {
    
        $auth = Yii::$app->authManager;
    
        $auth->loadFromCache();
    
        if ($auth->items !== null) {
            return $auth->checkAccessFromCache($userID, $permissionName, $params, $assignments);
        } else {
            return $auth->checkAccessRecursive($userID, $permissionName, $params, $assignments);
        }
    }
    
    public function assign($type, $role, $userID = null, $groupID = null)
    {
    
        switch ($type) {
    
            case 'group':
    
            // clear existing assigments
            self::revoke('group', $userID, $groupID);
    
            $groupAccess = new GroupAccess();
            $groupAccess->group_id = $groupID;
            $groupAccess->user_id = $userID;
            $groupAccess->item_name = $role;
            $groupAccess->created_date = time();
    
            return $groupAccess->save();
    
            break;
    
            case 'user':
    
            // clear existing assignments
            self::revoke('user', $userID);
    
            $userAccess = new UserAccess();
            $userAccess->user_id = $userID;
            $userAccess->item_name = $role;
            $userAccess->created_date = time();
    
            return $userAccess->save();
    
            break;
    
        }
    
    }
    
    public function revoke($type, $userID, $groupID = null)
    {
    
        switch ($type) {
    
            case 'group':
    
            GroupAccess::deleteAll('user_id = :user_id and group_id = :group_id', [':user_id' => $userID, ':group_id' => $groupID]);
    
            break;
    
            case 'user':
    
            UserAccess::deleteAll('user_id = :user_id', [':user_id' => $userID]);
    
            break;
    
        }
    
    }
    
    }
    

    以下是访问这些功能的一些示例用法:

    // get the user option
    echo Yii::$app->options->getOption('user', 'active_group_id');
    
    // assign group role
    Yii::$app->access->assign('group', 'group_creator', 22, 18);
    // assign user role
    Yii::$app->access->assign('user', 'app_user', 22);
    
    // revoke group access
    Yii::$app->access->revoke('group', 22, 18);
    // revoke user access
    Yii::$app->access->revoke('user', 22);
    
    // test user permission
    var_dump(Yii::$app->access->canUser('user', 'manage_app_settings'));
    // test the group permission
    var_dump(Yii::$app->access->canUser('group', 'manage_group_settings'));
    

    本质上,我从DbManager中复制了checkAccess函数并稍微重新设计了一下,以检查基于组的用户访问权限。

    唯一的问题是,我必须对实际的源DbManager类进行更改以使$ items(属性),checkAccessFromCache(函数)和checkAccessRecursive(函数)全部公开,以便可以在类。主要缺点是可更新性......

    有什么方法吗?

    感谢。

1 个答案:

答案 0 :(得分:2)

这是一个有效的最终解决方案。

所以,另一天,更多的重构。

我的最终解决方案使用DbManager / ManagerInterface源文件中的checkAccess函数,但我添加了要传递的$ assignments参数。主要问题是我必须建立自己的分配清单以供检查。确保注释掉设置$ assignments变量的行。

这是我的新访问模式:

<?php

namespace app\models;

use Yii;

class Access
{

public function canUser($type, $permissionName, $params = [])
{

    $auth = Yii::$app->authManager;

    switch ($type) {

        case 'group':

        $userID = Yii::$app->user->identity->id;
        $groupID = Yii::$app->options->getOption('user', 'active_group_id');

        $queryAll = GroupAccess::find()
        ->where('user_id = :user_id and group_id = :group_id', [':user_id' => $userID, ':group_id' => $groupID])
        ->asArray()
        ->all();

        $assignments = [];
        foreach ($queryAll as $queryItem) {
            $assignments[$queryItem['item_name']] = [
            'userId' => $queryItem['user_id'],
            'roleName' => $queryItem['item_name'],
            'createdAt' => $queryItem['created_date'],
            ];
        }

        $result = $auth->checkAccess($userID, $permissionName, $assignments, $params);

        return $result;

        break;

        case 'user':

        $userID = Yii::$app->user->identity->id;

        $queryAll = UserAccess::find()
        ->where('user_id = :user_id', [':user_id' => $userID])
        ->asArray()
        ->all();

        $assignments = [];
        foreach ($queryAll as $queryItem) {
            $assignments[$queryItem['item_name']] = [
            'userId' => $queryItem['user_id'],
            'roleName' => $queryItem['item_name'],
            'createdAt' => $queryItem['created_date'],
            ];
        }

        $result = $auth->checkAccess($userID, $permissionName, $assignments, $params);

        return $result;

        break;

    }

}

public function assign($type, $role, $userID = null, $groupID = null)
{

    switch ($type) {

        case 'group':

        // clear existing assigments
        self::revoke('group', $userID, $groupID);

        $groupAccess = new GroupAccess();
        $groupAccess->group_id = $groupID;
        $groupAccess->user_id = $userID;
        $groupAccess->item_name = $role;
        $groupAccess->created_date = time();

        return $groupAccess->save();

        break;

        case 'user':

        // clear existing assignments
        self::revoke('user', $userID);

        $userAccess = new UserAccess();
        $userAccess->user_id = $userID;
        $userAccess->item_name = $role;
        $userAccess->created_date = time();

        return $userAccess->save();

        break;

    }

}

public function revoke($type, $userID, $groupID = null)
{

    switch ($type) {

        case 'group':

        GroupAccess::deleteAll('user_id = :user_id and group_id = :group_id', [':user_id' => $userID, ':group_id' => $groupID]);

        break;

        case 'user':

        UserAccess::deleteAll('user_id = :user_id', [':user_id' => $userID]);

        break;

    }

}

}

这是DbManager中修改过的checkAccess函数:

public function checkAccess($userId, $permissionName, $assignments, $params = [])
{
    //$assignments = $this->getAssignments($userId);
    $this->loadFromCache();
    if ($this->items !== null) {
        return $this->checkAccessFromCache($userId, $permissionName, $params, $assignments);
    } else {
        return $this->checkAccessRecursive($userId, $permissionName, $params, $assignments);
    }
}

这是ManagerInterface.php中修改后的checkAccess函数:

public function checkAccess($userId, $permissionName, $assignments, $params = []);

我没有将$ items,checkAccessFromCache和checkAccessRecursive函数从protected更改为public。

这是我的UserAccess模型:

<?php

namespace app\models;

use Yii;
use yii\db\ActiveRecord;

 /**
 * This is the model class for table "app_user_access".
 *
 * @property integer $id
 * @property integer $user_id
 * @property string $item_name
 * @property integer $created_date
 *
 * @property AppAuthItem $itemName
 * @property AppUsers $user
 */
class UserAccess extends ActiveRecord
{
/**
 * @inheritdoc
 */
public static function tableName()
{
    return 'app_user_access';
}

/**
 * @inheritdoc
 */
public function rules()
{
    return [
        [['user_id', 'item_name', 'created_date'], 'required'],
        [['user_id', 'created_date'], 'integer'],
        [['item_name'], 'string', 'max' => 64]
    ];
}

/**
 * @inheritdoc
 */
public function attributeLabels()
{
    return [
        'id' => 'ID',
        'user_id' => 'User ID',
        'item_name' => 'Item Name',
        'created_date' => 'Created Date',
    ];
}

/**
 * @return \yii\db\ActiveQuery
 */
public function getItemName()
{
    return $this->hasOne(AppAuthItem::className(), ['name' => 'item_name']);
}

/**
 * @return \yii\db\ActiveQuery
 */
public function getUser()
{
    return $this->hasOne(AppUsers::className(), ['id' => 'user_id']);
}
}

以下是GroupAccess模型:

<?php

namespace app\models;

use Yii;
use yii\db\ActiveRecord;

 /**
 * This is the model class for table "app_group_access".
 *
 * @property integer $id
 * @property integer $group_id
 * @property integer $user_id
 * @property string $item_name
 * @property integer $created_date
 *
 * @property AppUsers $user
 * @property AppAuthItem $itemName
 * @property AppGroups $group
 */
class GroupAccess extends ActiveRecord
{
/**
 * @inheritdoc
 */
public static function tableName()
{
    return 'app_group_access';
}

/**
 * @inheritdoc
 */
public function rules()
{
    return [
        [['group_id', 'user_id', 'item_name', 'created_date'], 'required'],
        [['group_id', 'user_id', 'created_date'], 'integer'],
        [['item_name'], 'string', 'max' => 64]
    ];
}

/**
 * @inheritdoc
 */
public function attributeLabels()
{
    return [
        'id' => 'ID',
        'group_id' => 'Group ID',
        'user_id' => 'User ID',
        'item_name' => 'Item Name',
        'created_date' => 'Created Date',
    ];
}

/**
 * @return \yii\db\ActiveQuery
 */
public function getUser()
{
    return $this->hasOne(AppUsers::className(), ['id' => 'user_id']);
}

/**
 * @return \yii\db\ActiveQuery
 */
public function getItemName()
{
    return $this->hasOne(AppAuthItem::className(), ['name' => 'item_name']);
}

/**
 * @return \yii\db\ActiveQuery
 */
public function getGroup()
{
    return $this->hasOne(AppGroups::className(), ['id' => 'group_id']);
}
}

再一次,一些有用的样本:

// assign group role
Yii::$app->access->assign('group', 'group_creator', 24, 20);
// assign user role
Yii::$app->access->assign('user', 'app_user', 24);

// revoke group
Yii::$app->access->revoke('group', 22, 18);
// revoke user
Yii::$app->access->revoke('user', 22);

// test user permission
var_dump(Yii::$app->access->canUser('user', 'manage_app_settings'));
// test the group permission
var_dump(Yii::$app->access->canUser('group', 'manage_group_settings'));