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

标签 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' ], ];

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

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

这是一个自定义函数,可从用户选项表中提取事件组 ID。如果用户切换组,这将被更改。我的选项模型具有三种类型“app”、“user”、“group”。

如果我能找出一个与 native 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(函数)全部公开,以便可以在外部访问它们类(class)。主要缺点是可更新性...

有什么办法解决这个问题吗?

谢谢。

最佳答案

这是一个可行的最终解决方案。

所以,改天,更多的重构。

我的最终解决方案使用 DbManager/ManagerInterface 源文件中的 checkAccess 函数,但我添加了要传递的 $assignments 参数。主要问题是我必须建立自己的作业 list 以供检查。确保注释掉设置 $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'));

关于mysql - 基于组的每个用户的 Yii2 RBAC 多重分配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29381801/

相关文章:

PHP表单更新多个mysql行

python - 无法从 View 的列中获取数据

jquery - Yii2:检测 Pjax 表单提交与手动提交

jquery - yii2 : kartik-v/fileinput trigger upload on selection

codeigniter - 复杂的 RBAC Codeigniter 库(用于细粒度的用户权限和基于组的限制)

yii - Yii 的 RBAC 中的 "tasks"到底是什么?

php - 带有引号/撇号变体的 wordpress 查询

yii2 - Yii Gridview 在过滤记录时显示/隐藏界面中的列

java - 如何为不属于该组织的用户授予角色组织管理权限?

php - 使用 IP 地址作为主机的 PDO 连接