У админа может и не быть возможности к разрешению пользователя и в пределах одной роли пользователи могут иметь разный доступ к разрешениям
Как организовать сущности Role,Permission,Rule
Роли (role): типовые роли supper_admin,admin,customer (сотрудник, менеджер),user (авторизированный пользователь),guest (не авторизированный пользователь). Роль supper_admin наследует от всех ролей разрешения благодаря этому supper_admin имеет доступ ко всем permission не зависимо от их наличия в конкретной роли но требуется пропуск во всех правилах;
Разрешения (permission): роль является прямым родителем разрешения, без наследования (кроме роли supper_admin).Другими словами, одно и тоже разрешение будет назначаться каждой нужной роли.
Правила (Rule): правила для ролей и для разрешений наследуются от BaseRole в котором присутсвует проверка общих правил.
Админка для ролей.
Добавление, удаление, обновление разрешений.
Админка для разрешений.
Добавление, удаление.
Админка разрешения пользователя.
Тут должна быть возможность конкретному пользователю по мимо его разрешений и запрещающих ролей(запрещающие разрешения) назначить или снять определенное разрешение или запрещающюю роль(запрещающее разрешение).По поводу запрещающих ролей(запрещающих разрешений) будет пояснение дальше.
Подключение компонента: 'components' => [ .... 'authManager' => [ 'class' => 'yii\rbac\DbManager', 'itemTable' => 'auth_item', 'itemChildTable' => 'auth_item_child', 'assignmentTable' => 'auth_assignment', 'ruleTable' => 'auth_rule', 'defaultRoles' => ['guest'],// роль которая назначается всем пользователям по умолчанию ], ....]
Главное найте место в проекте где будет располагаться проверка, так как от этого зависит имена разрешений которые будут проверятся.
Самый простой это формировать ключи разрешений из action контроллера в который мы попадаем.Можно добавить в ключ админка это или фронтенд часть, название контроллера и метода и метод запроса GET,POST,PUT,DELETE… что бы сформировать уникальное название разрешения на всем сайте. К примеру fr_user_profile_get для фронтенда site.com.ua/user/profile методом GET
По ссылке ниже можно ознакомится с вариантами расположения проверки достпупа:
Альтернативная настройка RBAC
public function actionIndex() { if (!\Yii::$app->user->can('index')) { throw new ForbiddenHttpException('Access denied'); } return $this->render('index'); }
public function beforeAction($action) { if (parent::beforeAction($action)) { if (!\Yii::$app->user->can($action->id)) { throw new ForbiddenHttpException('Access denied'); } return true; } else { return false; } }
И так, вы уже определились с местом проверки разрешения.Теперь организуем логику.
Что такое запрещающая роль(запрещающее разрешение) — это по сути запрещающее разрешение, а
по факту запрещающая роль.Что бы была возможность конкретному пользователю запретить определенное разрешение.Дело в том что мы не можем назначить пользователю разрешение мы можем только роль назначить при чем даже и не одну.Вот тут мы и создаем роль именованную названием разрешения с постпрефиксом _not которая при наличии у пользователя будет запрещать доступ, а проверка на наличие этой роли-разрешения будет происходить в базом правиле от которого наследуются все правили для ролей и разрешений.
Проверка разрешения
/* При проверки разрешения мы передаем массив параметров конкретно в моем случае это класс который будет использован в правиле удаления/обновления */ if(!Yii::$app->user->can( 'ваш ключ разрешения',['class'=>static::class])){ throw new \yii\web\ForbiddenHttpException('Access denied role '); }
// RULES Yii::$app->authManager->removeAllRules(); //общая проверка во всех разрешениях без правил на отсутствие блокирующего разрешения $BaseRule= new \common\rbac\BaseRule(); Yii::$app->authManager->add($BaseRule); //только для разрешений $RuleUpdateDelete=new \common\rbac\RuleUpdateDelete(); Yii::$app->authManager->add($RuleUpdateDelete); // правило только для роли admin $RuleForAdmin= new \common\rbac\RuleForAdmin(); Yii::$app->authManager->add($RuleForAdmin); // правило только для роли customer $RuleForCustomer= new \common\rbac\RuleForCustomer(); Yii::$app->authManager->add($RuleForCustomer); // правило только для роли user $RuleForUser= new \common\rbac\RuleForUser(); Yii::$app->authManager->add($RuleForUser); // правило только для роли guest $RuleForGuest= new \common\rbac\RuleForGuest(); Yii::$app->authManager->add($RuleForGuest); // ROLES Yii::$app->authManager->removeAllRoles(); $role_supper_admin = Yii::$app->authManager->createRole('supper_admin'); $role_supper_admin->description='supper_admin'; Yii::$app->authManager->add($role_supper_admin); $role_admin = Yii::$app->authManager->createRole('admin'); $role_admin->description='Сотрудник admin'; $role_admin->ruleName=$RuleForAdmin->name; Yii::$app->authManager->add($role_admin); $role_customer = Yii::$app->authManager->createRole('customer'); $role_customer->description='Сотрудник customer'; $role_customer->ruleName=$RuleForCustomer->name; Yii::$app->authManager->add($role_customer); $role_user = Yii::$app->authManager->createRole('user');// авторизирован $role_user->description='Авторизированный пользователь'; $role_user->ruleName=$RuleForUser->name; Yii::$app->authManager->add($role_user); $role_guest = Yii::$app->authManager->createRole('guest');// не авторизирован $role_guest->description='Не авторизированный пользователь'; $role_guest->ruleName=$RuleForGuest->name; Yii::$app->authManager->add($role_guest); //наследование тольку у суппер админа Yii::$app->authManager->addChild($role_supper_admin, $role_admin); Yii::$app->authManager->addChild($role_supper_admin, $role_customer); Yii::$app->authManager->addChild($role_supper_admin, $role_user); Yii::$app->authManager->addChild($role_supper_admin, $role_guest);
При создании роли учесть
public function create(){ //общая проверка во всех разрешениях без правил на отсутствие блокирующего разрешения $BaseRule= new BaseRule(); $role_new = Yii::$app->authManager->createRole($this->role); $role_new->description=$this->description; if($this->data)$role_new->data=$this->data; // правило которое будет срабатывать при проверке на эту роль $role_new->ruleName=$BaseRule->name; Yii::$app->authManager->add($role_new); // Добавление разрешений if($role_new=Yii::$app->authManager->getRole($this->role)){ if(isset($this->permissions)){ foreach ($this->permissions as $permission=>$val){ $child= Yii::$app->authManager->getPermission($permission); if($child instanceof yii\rbac\Permission && Yii::$app->authManager->canAddChild($role_new, $child)){ Yii::$app->authManager->addChild($role_new, $child); } } } // Обязательно дабавляем новую роль к supper_admin так как он не имеет сових непосредственных разрешений $role_supper_admin=Yii::$app->authManager->getRole('supper_admin'); if(Yii::$app->authManager->canAddChild($role_supper_admin, $role_new)){ Yii::$app->authManager->addChild($role_supper_admin, $role_new); } return true; }else{ return false; } }
При создании разрешения учесть
public function create() { /* После валидации и инициализации аттрибутов мы имеем $this->permission название разрешения из котого мы должны понять какое правило ему назначить базовое или для удаление/обновления которое также наследуется от базового. Правило для удаления и обновления должно по мимо проверки запрещающих разрешений еще проверять имеет ли конкретный пользователь к изменяемой информации (т.е. если пользователь этот комментарий создал то он может его и удалить или изменить но другой пользователь кроме supper_admin,admin,customer) В $permission->data можете сохранять полезню информацию о составляющих вашего ключа что б при редактировании легче можно было найти источник его. */ if(preg_match('#.*(Delete|Put)$#', $this->method) ){ $Rule=Yii::$app->authManager->getRule('RuleUpdateDelete'); }else{ $Rule=Yii::$app->authManager->getRule('BaseRule'); } $permission = Yii::$app->authManager->createPermission($this->permission); $permission->description = $this->description; // правило которое будет срабатывать при проверке на это разрешение $permission->ruleName = $Rule->name; $permission->data = [....];// ваши вспомогательные данные Yii::$app->authManager->add($permission); //Создаем роль-разрешение с поспрефиксом _not $role_not = Yii::$app->authManager->createRole($this->permission.'_not'); $role_not->description = 'Для закрытия разрешения '.$this->permission; Yii::$app->authManager->add($role_not); //Добавил в таблицу gr_auth_item поле isnot показывающее роли ли это по сути return Yii::$app->db->createCommand("UPDATE `gr_auth_item` SET `isnot`= 1 WHERE type=1 AND name=:name") ->bindValue(":name", $this->permission.'_not',PDO::PARAM_STR) ->execute(); }
Базовое правило
/* Проверка на роль supper_admin и запрещающее разрешение что требуется у всех ролей */ class BaseRule extends \yii\rbac\Rule { public $name ='BaseRule'; public function execute($user_id, $role, $params) { if(Yii::$app->user->can('supper_admin') )return 1; // при налии блокирующего разрешения у пользователя if(Yii::$app->user->can($role->name.'_not') )return false; //Даже у роли admin и manager может быть блокирующее разрешение return true; } }
Базовое правило роли
//Правило для конкретной роли (присутствует у каждой роли кроме supper_admin) //срабатывает при проверке на причастность к роли ...->can('user') // к примеру user /* Обычная проверка на причастность к самой роли и базовая проверка от BaseRule */ class RuleForUser extends BaseRule { public $name='RuleForUser' ; public function execute($user_id, $role, $params) { $parent= parent::execute($user_id, $role, $params); if($parent===1)return true; if($parent==false)return false; if( isset(Yii::$app->authManager->getRolesByUser($user_id)[$role->name]) )return true; return false; } }
Правило требующее проверки на изменение
/* В нем мы также наследуемся от BaseRule */ class RuleUpdateDelete extends BaseRule { public $name = 'RuleUpdateDelete' ; public function execute($user_id, $permission, $params) { // пропускаем базовые проверки $parent= parent::execute($user_id, $permission, $params); if($parent===1)return true; if($parent==false)return false; // пропускаем такие роли как admin и customer if(Yii::$app->user->can('admin') || Yii::$app->user->can('customer'))return true; if(isset($params['class']) && method_exists($params['class'], 'can') ){ // проверка принадлежности пользователя к изменяемому объекту if(method_exists($params['class'], 'can')) return $params['class']::can($user_id); else return false; } return false; } }
От куда берется аргумент $params в методе execute?
Когда мы выполняем проверку
if(!Yii::$app->user->can( 'ваш ключ разрешения',['class'=>static::class])){ throw new \yii\web\ForbiddenHttpException('Access denied role '); }
мы передаем вторым параметром массив.В мое случае это класс который я использую для вызова метода одноименного метода can в этом классе для проверки принадлежности конкретного пользователя к изменяемому объекту
Итог
Должены получить функционал с помощью которого можно гибко управлять правами и доступом ко всему сайту иметь возможность без ограничений получить полный доступ с supper_admin ролью, гибко настраивать разрешения у конкретных пользователей.
good luck, Jekshmek
ссылка на оригинал статьи https://habrahabr.ru/post/327170/
Добавить комментарий