问题描述
从技术上讲,我的应用程序有两个区域,一个全局区域(反馈,用户配置文件,用户设置等)和一个组区域(联系人,项目,组配置文件,组设置等).
My application technically has two areas, a global area (feedback, user profile, user settings, etc) and a group area (contacts, projects, group profile, group settings, etc).
我正在将RBAC DBManager用于全局区域,并且工作正常,但是在实现针对组区域的授权机制时遇到了问题.
I am using the RBAC DBManager for the global area, and it works just fine, but I am having issues implementing an authorization mechanism for the group area.
原因是,组可以在用户之间共享,并且用户可能在group_access表中具有多个分配(id,group_id,user_id,item_name),因为它们可能是多个组的成员,并且它们可能具有不同的权限这些组的权限级别.
The reason, is that groups can be shared among the users, and a user may have multiple assignments in the group_access table (id, group_id, user_id, item_name) as they may be members of multiple groups, and they may have different permission levels for those groups.
这是我的身份验证设置:
Here is my auth setup:
$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列不是唯一的.
My group_access table has the same schema as the auth_assignment table, with the exception that it has a group_id column, and the user_id column is NOT unique.
用户将只有一个与全局区域有关的分配,但是在组区域上可能会有许多不同的分配,因为他们可能在组a上具有管理特权,而在组b上仅具有用户特权.
The user will only have one assignment concerning the global area, but may have many different assigments on the group area as they might have admin privelidges on group a, but only user privielidges on group b.
我的数据库设置如下:
-
用户(状态ID,用户名,身份验证密钥,密码哈希,电子邮件等)
Users (status_id, username, auth_key, password_hash, email, etc)
组(状态ID,名称,描述等)
Groups (status_id, name, description, etc)
Group_Access(group_id,user_id,item_name)每个用户为他们有权访问的每个组分配一个分配.
Group_Access (group_id, user_id, item_name) Each user gets one assignment for each group they have access to.
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' ],];
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函数可以限定userID,我什至可以使用较短的"can"版本,该版本对已登录用户非常有用,但是我需要根据如下用户选项检查访问权限:
The checkAccess function can qualify the userID, and I can even use the shorter "can" version which works great for the logged in user, but I need to check access based on a user option like below:
Option::getOption('user', 'active_group_id')
这是一个自定义函数,可从用户选项表中提取活动组ID.如果用户切换组,这将被更改.我的选项模型具有三种类型:应用",用户",组".
This is a custom function that pulls the active group id from a user options table. If a user switches groups, this will be changed. My options model has three types 'app', 'user', 'group'.
如果我能找出一个与本机checkAccess相同的功能,但称为checkGroupAccess并自动获取active_group_id并从group_access表中提取用户分配并执行权限检查,那就太好了.
It would be nice if I could figure out a function that works the same was as the native checkAccess but be called checkGroupAccess and automatically get the active_group_id and pull the user assignments from the group_access table and perform the permission check.
我希望这是有道理的.
谢谢您的时间.
迈克
**更新**
因此,我有一个解决方案,它使用自定义的checkAccess函数来检查组或全局区域上的适当权限.
So, I have a solution, that uses custom checkAccess functions to check for proper permissions on the group or global areas.
我有两个表(user_access,group_access)与默认的{{auth_assignment}}表具有相似的架构,而我现在不使用它们.我正在使用{{auth_item}},{{auth_item_child}}和{{auth_rule}}表.
I have two tables (user_access, group_access) that have a similar schema to the default {{auth_assignment}} table, of which I am not using now. I am using the {{auth_item}}, {{auth_item_child}}, and {{auth_rule}} tables.
我有两个模型,每个访问表分别为GroupAccess => group_access和UserAccess => user_access.
I have two models, one for each of the access tables GroupAccess => group_access, and UserAccess => user_access.
我还具有访问功能的模型,并将其映射到组件配置.
I also have a model for the access functions and have mapped it to the components configuration.
这是我的访问模式:
<?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;
}
}
}
以下是一些访问函数的示例:
And here are some sample uses to access the functions:
// 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函数,并对其进行了一些重做以根据组检查用户访问权限.
In essence, I copied the checkAccess function from the DbManager and reworked it a little to check for user access based on group.
唯一的问题是,我必须对实际的源DbManager类进行更改,以使$ items(属性),checkAccessFromCache(函数)和checkAccessRecursive(函数)全部公开,以便可以在外部访问它们.班级.主要缺点是可更新性...
The only issue, is that I had to make a change to the actual source DbManager class to make the $items (property), checkAccessFromCache (function), and checkAccessRecursive (function) all public so they can be accessed outside of the class. The main drawback is updateability...
有什么办法解决吗?
谢谢.
推荐答案
这是可行的最终解决方案.
Here is a working final solution.
因此,改天,更多重构.
So, another day, more refactoring.
我的最终解决方案在DbManager/ManagerInterface源文件中使用checkAccess函数,但是我添加了$ assignments参数来传递.主要问题是我必须建立自己的作业列表进行检查.确保注释掉设置$ assignments变量的行.
My final solution uses the checkAccess function in the DbManager/ManagerInterface source files, but I added the $assignments parameter to be passed. The main issue is that I had to build my own assignments list for checking. Make sure you comment out the lines where the $assignments variable is set.
这是我的新访问模式:
<?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函数:
And here is the modified checkAccess function in DbManager:
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函数:
And here is the modified checkAccess function in ManagerInterface.php:
public function checkAccess($userId, $permissionName, $assignments, $params = []);
我没有将$ items,checkAccessFromCache和checkAccessRecursive函数从受保护的状态更改为公共.
I did not change the $items, checkAccessFromCache, and checkAccessRecursive functions to public from protected.
这是我的UserAccess模型:
And here is my UserAccess model:
<?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模型:
And here is the the GroupAccess Model:
<?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']);
}
}
再次提供一些有用的示例:
And once again, some useful samples:
// 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'));
这篇关于基于组的每个用户的Yii2 RBAC多个分配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!