У админа может и не быть доступа к разрешению пользователя и в пределах одной роли пользователи могут иметь разный доступ к разрешениям
Как организовать сущности 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
Способ 1 — в методе контроллера
public function actionIndex()
{
if (!\Yii::$app->user->can('index')) {
throw new ForbiddenHttpException('Access denied');
}
return $this->render('index');
}
Способ 2 — прописать beforeAction
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);
При создании роли учесть
create
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;
}
}
При создании разрешения учесть
create
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
$permission_not = Yii::$app->authManager->createPermission($this->permission.'_not');
$permission_not ->description = 'Для закрытия разрешения '.$this->permission;
Yii::$app->authManager->add($permission_not );
//Добавил в таблицу gr_auth_item поле isnot показывающее тип permission
return Yii::$app->db->createCommand("UPDATE `gr_auth_item` SET `isnot`= 1 WHERE type=2 AND name=:name")
->bindValue(":name", $this->permission.'_not',PDO::PARAM_STR)
->execute();
}
Базовое правило
BaseRule
/*
Проверка на роль supper_admin и запрещающее разрешение,
это требуется у всех ролей
*/
class BaseRule extends \yii\rbac\Rule
{
public $name ='BaseRule';
public function execute($user_id, $permission, $params)
{
if(Yii::$app->user->can('supper_admin') )return 1;
// при налии блокирующего разрешения у пользователя
if(Yii::$app->user->can($permission->name.'_not') )return false;
//Даже у роли admin и manager может быть блокирующее разрешение
return true;
}
}
Базовое правило роли
RuleForUser
//Правило для конкретной роли (присутствует у каждой роли кроме 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;
}
}
Правило требующее проверки на изменение
RuleUpdateDelete
Какую работу выполняет параметр $params в методе execute?
Когда мы выполняем проверку
мы передаем вторым параметром массив. В мое случае, это класс, который я использую для вызова одноименного метода can в этом классе, для проверки принадлежности конкретного пользователя к изменяемому объекту.
/*
В нем мы также наследуемся от 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