我的应用程序在技术上有两个区域,全局区域(反馈,用户配置文件,用户设置等)和组区域(联系人,项目,组配置文件,组设置等)。
我正在使用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上具有用户权限。
我的数据库设置如下:
用户(status_id,用户名,auth_key,password_hash,电子邮件等)
组(status_id,名称,描述等)
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函数可以限定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(函数)全部公开,以便可以在类。主要缺点是可更新性......
有什么方法吗?
感谢。
答案 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'));