Yii2 RBAC Multiple assignments for each user based on groups

My application technically has two areas: the global area (feedback, user profile, user settings, etc.) and the group area (contacts, projects, group profile, group settings, etc.).

I use RBAC DBManager for the global scope and it works very well, but I am having problems implementing the authorization mechanism for the group scope.

The reason is that groups can be divided between users, and the user can have several assignments in the group_access table (id, group_id, user_id, item_name), because they can be members of several groups, and they can have different permission levels for these groups .

Here is my auth installation:

$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); 

The group_access table has the same layout as the auth_assignment table, except that it has a group_id column and the user_id column is NOT unique.

The user will have only one task pertaining to the global area, but can have many different attributes in the group area, since they can have administrative privileges in group a, but only user privileges in group b.

My DB is configured like this:

  • Users (status_id, username, auth_key, hash password, email, etc.)

  • Groups (status_id, name, description, etc.)

  • Group_Access (group_id, user_id, item_name) Each user receives one task for each group to which they have access.

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

The checkAccess function can qualify the user ID, and I can even use the shorter version of "can", which is great for the registered user, but I need to check access based on the user option, as shown below:

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

This is a custom function that pulls the active group identifier from the user options table. If the user switches groups, this will be changed. My model has three types: "application", "user", "group".

It would be nice if I could find a function that works the same as native checkAccess, but checkGroupAccess will be called and it will automatically get active_group_id and pull the user destinations from the group_access table and check the rights check.

Hope this makes sense.

Thank you for your time.

Mike

** UPDATED **

So, I have a solution that uses the checkAccess custom functions to check the correct permissions for a group or global areas.

I have two tables (user_access, group_access) that have a similar scheme to the default table {{auth_assignment}}, which I do not use now. I use the tables {{auth_item}}, {{auth_item_child}} and {{auth_rule}}.

I have two models: one for each access table GroupAccess => group_access and UserAccess => user_access.

I also have a model for access functions and mapped to component configuration.

Here is my access model:

 <?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 usage examples for accessing 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')); 

In essence, I copied the checkAccess function from DbManager and reworked it a bit to check user access based on the group.

The only problem is that I had to make changes to the actual DbManager source class to make the objects (property), checkAccessFromCache (function) and checkAccessRecursive (function) public so that they could be accessed outside the class. The main disadvantage is the ability to update ...

How to get around this?

Thanks.

+7
mysql rbac yii2
source share
1 answer

Here is the current final decision.

So, one more day, more refactoring.

My final solution uses the checkAccess function in the DbManager / ManagerInterface source files, but I added the $ assignments parameter, which should be passed. The main problem is that I had to create my own list of appointments for verification. Make sure you comment out the lines in which the $ assignments variable is set.

Here is my new access model:

 <?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; } } } 

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); } } 

And here is the modified checkAccess function in ManagerInterface.php:

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

I did not change the functions $ items, checkAccessFromCache and checkAccessRecursive for the public from protected.

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']); } } 

And here is 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 again, some useful examples:

 // 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')); 
+2
source share

All Articles