Сервис на Yii2: Добавление RBAC для разграничения прав
Недавно мы переработали модульную структуру нашего сервиса. А нас уже есть модуль администрирования, который необходимо закрыть от посторонних глаз. Вдальнейшем мы будем добавлять новые ограничения для разных пользователей, поэтому пришла пора добавить контроль доступа на основе ролей.
Предыдущие части | Исходники на GitHub
Сейчас наша панель управления закрыта на примитивном уровне только от незалогиненных пользователей:
namespace app\modules\admin; use yii\filters\AccessControl; class Module extends \yii\base\Module { ... public function behaviors() { return [ 'access' => [ 'class' => AccessControl::className(), 'rules' => [ [ 'allow' => true, 'roles' => ['@'], ], ], ], ]; } ... }
Но нам нужно будет сделать так, чтобы он был доступен только администраторам.
Как мы уже рассматривали в вебинаре по RBAC, в Yii2 имеется два встроенных менеджера по управлению ролями и привязке их к пользователю. Это PhpManager
, хранящий данные в виде массивов в файлах, и DbManager
, сохраняющий всё в таблицах в базе данных. Мы подробно обсудили плюсы и минусы каждого подхода в том видео, поэтому здесь теории будет не очень много. Рассмотрим практику внедрения RBAC в проект.
В любом случае, достаточно подключить любой из них как компонент authManager
в конфигурационном файле:
'components' => [ ... 'authManager' => [ 'class' => 'yii\rbac\PhpManager', ], ],
и его методами вроде таких:
$auth = Yii::$app->authManager; $user = $auth->createRole('user'); $user->description = 'User'; $auth->add($user); $admin = $auth->createRole('admin'); $admin->description = 'Admin'; $auth->add($admin); $auth->addChild($admin, $user);
создавать роли и методом $auth->assign($role, $userId)
привязывать роль к пользователю. А сам менеджер уже будет сохранять эту информацию в своих файлах или таблицах.
Для нашего случая нужны всего две роли user
и admin
. Можно создать только их и проверять доступ по этим ролям. Но более гибкий и предпочтительный вариант – это использование так называемых «разрешений» вроде adminPanel
и подобных и привязка их к ролям.
Использование имён разрешений вроде
adminPanel
в модулях предпочтительнее имён ролей вродеuser
илиadmin
, так как на каждом сайте можно будет делать разных админов и пользователей, в любой последовательности привязывая к ним ваши разрешения.
Чтобы не создавать всё вручную, можно создать консольный контроллер, в действии которого производить всё построение ролей и разрешений:
namespace app\commands; use Yii; use yii\console\Controller; /** * RBAC generator */ class RbacController extends Controller { /** * Generates roles */ public function actionInit() { $auth = Yii::$app->getAuthManager(); $auth->removeAll(); $adminPanel = $auth->createPermission('adminPanel'); $adminPanel->description = 'Admin panel'; $auth->add($adminPanel); $user = $auth->createRole('user'); $user->description = 'User'; $auth->add($user); $admin = $auth->createRole('admin'); $admin->description = 'Admin'; $auth->add($admin); $auth->addChild($admin, $user); $auth->addChild($admin, $adminPanel); $this->stdout('Done!' . PHP_EOL); } }
Здесь мы создаём роли user
и admin
, для простоты наследуем admin
от user
(чтобы администратор мог делать всё, что позволено пользователю). Также создаём разрешение adminPanel
, по которому будем проверять доступ в панель управления, и присваиваем это разрешение только роли admin
.
Может показаться более логичным поместить работу с RBAC в модуль
user
, но разрешения и роли обычно оказываются раскиданными по всему проекту. Поэтому удобнее оставить в модулях только их специфичные разрешения, а создание ролей оставить на уровне самого приложения.
Сейчас мы все названия ролей и разрешений задаём жёстко вписанными строками. Роли мы опишем только один раз, но имена разрешений мы будем использовать и в других местах. Такие повторяющиеся в разных фрагментах кода наименования удобнее выносить в константы, чтобы избавиться от ошибок с переименованиями и опечатками при копировании.
Сейчас у нас есть всего одно разрешение, относящееся к модулю admin
. А мы договорились, что всё, что относится к конкретному модулю мы будем помещать в его директорию. Так что создадим в модуле папку rbac
и создадим там класс с константой:
namespace app\modules\admin\rbac; class Rbac { const PERMISSION_ADMIN_PANEL = 'permAdminPanel'; }
Не стоит бояться длинных наименований, так как автоподстановка в любой IDE сразу покажет весь список.
Теперь впишем наше значение в фильтр модуля:
namespace app\modules\admin; use app\modules\admin\rbac\Rbac as AdminRbac; use yii\filters\AccessControl; use Yii; class Module extends \yii\base\Module { ... public function behaviors() { return [ 'access' => [ 'class' => AccessControl::className(), 'rules' => [ [ 'allow' => true, 'roles' => [Rbac::PERMISSION_ADMIN_PANEL], ], ], ], ]; } ... }
Также в пункт главного меню в views/layouts/main.php
:
use app\modules\admin\rbac\Rbac as AdminRbac; ... echo Nav::widget([ 'options' => ['class' => 'navbar-nav navbar-right'], 'activateParents' => true, 'items' => array_filter([ ['label' => Yii::t('app', 'NAV_HOME'), 'url' => ['/main/default/index']], ... Yii::$app->user->can(AdminRbac::PERMISSION_ADMIN_PANEL) ? ['label' => Yii::t('app', 'NAV_ADMIN'), 'url' => ['/admin/default/index']] : false, ... ]), ]); NavBar::end();
и в наш консольный контроллер:
use app\modules\admin\rbac\Rbac as AdminRbac; ... class RbacController extends Controller { /** * Generates roles */ public function actionInit() { $auth = Yii::$app->getAuthManager(); $auth->removeAll(); $adminPanel = $auth->createPermission(AdminRbac::PERMISSION_ADMIN_PANEL); $adminPanel->description = 'Admin panel'; $auth->add($adminPanel); $user = $auth->createRole('user'); $user->description = 'User'; $auth->add($user); $admin = $auth->createRole('admin'); $admin->description = 'Admin'; $auth->add($admin); $auth->addChild($admin, $user); $auth->addChild($admin, $adminPanel); $this->stdout('Done!' . PHP_EOL); } }
Одно из достоинств наличия класса-справочника с константами – возможность быстро просмотреть весь список в одном месте. Не нужно каждый раз искать по контроллерам и представлениям какие же правила имеются в данном модуле. Да и можно спокойно переименовать константу средствами самой IDE, и во всём проекте она переименуется автоматически.
Роли и разрешения готовы. Теперь их надо как-то присвоить пользователям. Сделаем для этого ещё один консольный контроллер:
namespace app\commands; use app\modules\user\models\User; use Yii; use yii\console\Controller; use yii\console\Exception; use yii\helpers\ArrayHelper; /** * Interactive console roles manager */ class RolesController extends Controller { /** * Adds role to user */ public function actionAssign() { $username = $this->prompt('Username:', ['required' => true]); $user = $this->findModel($username); $roleName = $this->select('Role:', ArrayHelper::map(Yii::$app->authManager->getRoles(), 'name', 'description')); $authManager = Yii::$app->getAuthManager(); $role = $authManager->getRole($roleName); $authManager->assign($role, $user->id); $this->stdout('Done!' . PHP_EOL); } /** * Removes role from user */ public function actionRevoke() { $username = $this->prompt('Username:', ['required' => true]); $user = $this->findModel($username); $roleName = $this->select('Role:', ArrayHelper::merge( ['all' => 'All Roles'], ArrayHelper::map(Yii::$app->authManager->getRolesByUser($user->id), 'name', 'description')) ); $authManager = Yii::$app->getAuthManager(); if ($roleName == 'all') { $authManager->revokeAll($user->id); } else { $role = $authManager->getRole($roleName); $authManager->revoke($role, $user->id); } $this->stdout('Done!' . PHP_EOL); } /** * @param string $username * @throws \yii\console\Exception * @return User the loaded model */ private function findModel($username) { if (!$model = User::findOne(['username' => $username])) { throw new Exception('User is not found'); } return $model; } }
Теперь запустим действие привязки роли:
php yii roles/assign
Он попросит нас ввести логин пользователя и роль.
После этого можно увидеть, что всё у нас попало в assignments.php
:
return [ 1 => [ 'admin', ], ];
Пользователю с ID=1 присвоилась роль admin
.
Если подключить аналогично DbManager
, применить его миграции, выполнить php yii rbac/init
и привязать аналогично роль к пользователю, то это всё появится в соответствующих таблицах в базе данных.
Для любого сайта достаточно выбрать
PhpManager
илиDbManager
и использовать его. Это стандартная практика, применимая повсюду. Если же хочется рассмотреть другие альтернативы, то об одной из них поговорим в этой статье.
Но при использовании DbManager
нам не особо хочется дёргать каждый раз базу данных для перебора ролей и разрешений.
Менеджер
PhpManager
удобен высокой производительностью, так как он загружает все роли одним запросом к своим файлам вместо рекурсивных запросов к базе данных. Но при большом числе пользователей файлassignments.php
станет большим и неповоротливым.
Мы можем пойти альтернативным путём, храня привязку к роли прямо в поле role
модели User
и используя подход с ролями по умолчанию defaultRoles
и распределением ролей общим правилом GroupRule
. Но это «костыль», так как с ним не будут работать getRolesByUser()
, assign()
и прочие системные методы.
Вместо этого можно схитрить, как мы и говорили в вебинаре. А именно, оставить все роли в файлах, а хранение роли поместить в поле role
в модели пользователя и доработать PhpManager
на получение роли из этого поля (вместо файла assignments.php
). Всё равно по стандартам чистого RBAC пользователю не нужно присваивать несколько ролей.
Итак, создадим миграцию, добавляющую поле role
и заполняющее его ролью user
:
use yii\db\Migration; class m160310_085103_add_user_role_field extends Migration { public function up() { $this->addColumn('{{%user}}', 'role', $this->string(64)); $this->update('{{%user}}', ['role' => 'user']); } public function down() { $this->dropColumn('{{%user}}', 'role'); } }
Теперь напишем свой гибридный AuthManager
. В Yii2 менеджер монолитен, так что просто поменять хранилище ролей и привязок к пользователю мы не сможем. Вместо этого поправим функциональность самого PhpManager
.
Отнаследуемся от класса yii\rbac\PhpManager
и переопределим те методы, которые работают с его полем $this->_assignments
. В них будем напрямую обращаться к нашей модели, а не брать значения, полученные из файла assignments.php
. В примитивном случае для определения роли нужно переопределить только метод getAssignments
, так как в методе checkAccess
вызывается именно он. В итоге достаточно такого переопределения:
namespace app\components; use app\modules\user\models\User; use yii\rbac\Assignment; use yii\rbac\PhpManager; use Yii; class AuthManager extends PhpManager { public function getAssignments($userId) { if ($userId && $user = $this->getUser($userId)) { $assignment = new Assignment(); $assignment->userId = $userId; $assignment->roleName = $user->role; return [$assignment->roleName => $assignment]; } return []; } /** * @param integer $userId * @return null|\yii\web\IdentityInterface|User */ private function getUser($userId) { $webUser = Yii::$app->get('user', false); if ($webUser && !$webUser->getIsGuest() && $webUser->getId() == $userId) { return $webUser->getIdentity(); } else { return User::findOne($userId); } } }
Но такой менеджер слишком примитивен. Его можно использовать только для чтения ролей, а не для их изменения. Для большей совместимости с оригинальным классом переопределим и методы вроде assign
и revoke
:
namespace app\components; use app\modules\user\models\User; use yii\rbac\Assignment; use yii\rbac\PhpManager; use Yii; class AuthManager extends PhpManager { public function getAssignments($userId) { if ($userId && $user = $this->getUser($userId)) { $assignment = new Assignment(); $assignment->userId = $userId; $assignment->roleName = $user->role; return [$assignment->roleName => $assignment]; } return []; } public function getAssignment($roleName, $userId) { if ($userId && $user = $this->getUser($userId)) { if ($user->role == $roleName) { $assignment = new Assignment(); $assignment->userId = $userId; $assignment->roleName = $user->role; return $assignment; } } return null; } public function getUserIdsByRole($roleName) { return User::find()->where(['role' => $roleName])->select('id')->column(); } public function assign($role, $userId) { if ($userId && $user = $this->getUser($userId)) { $assignment = new Assignment([ 'userId' => $userId, 'roleName' => $role->name, 'createdAt' => time(), ]); $this->setRole($user, $role->name); return $assignment; } return null; } public function revoke($role, $userId) { if ($userId && $user = $this->getUser($userId)) { if ($user->role == $role->name) { $this->setRole($user, null); return true; } } return false; } public function revokeAll($userId) { if ($userId && $user = $this->getUser($userId)) { $this->setRole($user, null); return true; } return false; } /** * @param integer $userId * @return null|\yii\web\IdentityInterface|User */ private function getUser($userId) { $webUser = Yii::$app->get('user', false); if ($webUser && !$webUser->getIsGuest() && $webUser->getId() == $userId) { return $webUser->getIdentity(); } else { return User::findOne($userId); } } /** * @param User $user * @param string $roleName */ private function setRole(User $user, $roleName) { $user->role = $roleName; $user->updateAttributes(['role' => $roleName]); } }
В конфигурационном файле config/common.php
поменяем теперь оригинальный PhpManager
на свой:
'components' => [ ... 'authManager' => [ 'class' => 'app\components\AuthManager', ], ],
Теперь можно попробовать перегенерировать роли:
php yii rbac/init
и заново попробовать привязать их к пользователям:
php yii roles/assign
При этом файл assignments.php
должен остаться пустым, а поле role
в таблице user
в базе заполниться новой ролью.
Хранение ролей мы организовали и поле role
в таблицу добавили. Осталось добавить вывод и установку роли в админку.
Управление ролями
Для отображения роли и присваивания её при управлении пользователями добавим правила валидации и надпись для поля role
в модель User
:
namespace app\modules\user\models; ... class User extends ActiveRecord implements IdentityInterface { ... public function rules() { return [ ... ['role', 'string', 'max' => 64], ]; } public function attributeLabels() { return [ ... 'role' => Module::t('module', 'USER_ROLE'), ]; } }
И в модели backend\User
добавим это поле в сценарии валидации:
namespace app\modules\user\models\backend; ... class User extends \app\modules\user\models\User { ... public function scenarios() { $scenarios = parent::scenarios(); $scenarios[self::SCENARIO_ADMIN_CREATE] = ['username', 'email', 'status', 'role', 'newPassword', 'newPasswordRepeat']; $scenarios[self::SCENARIO_ADMIN_UPDATE] = ['username', 'email', 'status', 'role', 'newPassword', 'newPasswordRepeat']; return $scenarios; } }
Аналогично добавим новое поле в поисковую модель backend\search\UserSearch
:
$query->andFilterWhere([ 'id' => $this->id, 'status' => $this->status, 'role' => $this->role, ]);
И в форму в представлении modules/user/views/backend/_form.php
добавим выпадающий список ролей:
<div class="user-form"> <?php $form = ActiveForm::begin(); $form->field($model, 'username')->textInput(['maxlength' => true]) ... $form->field($model, 'status')->dropDownList(User::getStatusesArray()) $form->field($model, 'role')->dropDownList(ArrayHelper::map(Yii::$app->authManager->getRoles(), 'name', 'description')) ... ActiveForm::end(); </div>
В Yii нет построителя форм, в который можно было бы спрятать такие повороты судьбы с дёрганием authManager
из представления. Если бы мы использовали отдельную модель для формы, то бы тоже могли спрятать это туда. Чтобы это не выглядело так кощунственно, лучше передать массив для выпадающего списка из контроллера:
<?= $form->field($model, 'role')->dropDownList($rolesList)
или даже сочинить свой InputWidget. Ну это сейчас не так важно.
Здесь мы могли бы последовать принципам профессиональной разработки и переписать модуль user
, но управление пользователями – это не главная часть нашего приложения. Выделением слоя предметной области, отделением его от слоя отображения и продумыванием архитектуры мы займёмся в основном модуле проектов. А здесь вместо настоящих моделей оставим стандартные классы ActiveRecord и обычный CRUD.
Теперь нам нужно вывести роль в GridView
. Для этого сделаем колонку, которая будет отображать описание роли, выделяя привилегированных пользователей красными бейджиками:
namespace app\modules\user\widgets\backend\grid; use yii\grid\DataColumn; use yii\helpers\Html; use Yii; class RoleColumn extends DataColumn { public $defaultRole = 'user'; protected function renderDataCellContent($model, $key, $index) { $value = $this->getDataCellValue($model, $key, $index); $label = $value ? $this->getRoleLabel($value) : $value; $class = $value == $this->defaultRole ? 'primary' : 'danger'; $html = Html::tag('span', Html::encode($label), ['class' => 'label label-' . $class]); return $value === null ? $this->grid->emptyCell : $html; } private function getRoleLabel($roleName) { if ($role = Yii::$app->authManager->getRole($roleName)) { return $role->description; } else { return $roleName; } } }
И подключим эту колонку в наш modules/user/views/backend/index.php
:
<?= GridView::widget([ 'dataProvider' => $dataProvider, 'filterModel' => $searchModel, 'columns' => [ 'id', ... [ 'class' => LinkColumn::className(), 'attribute' => 'username', ], 'email:email', ... [ 'class' => RoleColumn::className(), 'filter' => ArrayHelper::map(Yii::$app->authManager->getRoles(), 'name', 'description'), 'attribute' => 'role', ], ['class' => ActionColumn::className()], ], ]);
Чтобы видеть все присвоенные роли:
Теперь дополним фронтенд.
Доработка формы регистрации
Дополним наш модуль новым параметром $defaultRole
:
namespace app\modules\user; use Yii; class Module extends \yii\base\Module { /** * @var string */ public $defaultRole = 'user'; /** * @var int */ public $emailConfirmTokenExpire = 259200; // 3 days /** * @var int */ public $passwordResetTokenExpire = 3600; public static function t($category, $message, $params = [], $language = null) { return Yii::t('modules/user/' . $category, $message, $params, $language); } }
В саму форму регистрации добавим поле $_defaultRole
, которое будем передавать в конструктор и присваивать пользователю:
namespace app\modules\user\models\frontend\form; use app\modules\user\models\User; use app\modules\user\Module; use yii\base\Model; use Yii; /** * Signup form */ class SignupForm extends Model { public $username; public $email; public $password; public $verifyCode; private $_defaultRole; public function __construct($defaultRole, $config = []) { $this->_defaultRole = $defaultRole; parent::__construct($config); } ... public function signup() { if ($this->validate()) { $user = new User(); $user->username = $this->username; $user->email = $this->email; $user->setPassword($this->password); $user->status = User::STATUS_WAIT; $user->role = $this->_defaultRole; $user->generateAuthKey(); $user->generateEmailConfirmToken(); ... return $user; } return null; } }
И в контроллере будем передавать в форму этот параметр через конструктор:
namespace app\modules\user\controllers\frontend; ... class DefaultController extends Controller { ... public function actionSignup() { $model = new SignupForm($this->module->defaultRole); if ($model->load(Yii::$app->request->post())) { if ($user = $model->signup()) { Yii::$app->getSession()->setFlash('success', Module::t('module', 'FLASH_EMAIL_CONFIRM_REQUEST')); return $this->goHome(); } } return $this->render('signup', [ 'model' => $model, ]); } }
На этом с ролями пока всё.
Мы добавили к проекту поддержку ролей и разрешений. При этом мы сделали собственный «гибридный» вариант хранения ролей, скрестив PhpManager
с полем role
в таблице модели. При этом все стандартные методы вроде assign
, revoke
и getRolesByUser
остались полностью рабочими.
В следующей части подготовим этот компонент для совместного использования в других проектах, научившись выкладыввть свои расширения на GitHub и публиковать их как пакеты Composer:
Публикация расширений на GitHub и Packagist
Не забудьте подписаться на рассылку статей в сайдбаре или вместе с нами записаться и «позажигать» на бесплатных вебинарах. До встречи!
> Но такой менеджер слишком примитивен.
Это не менеджер, а вы просто не туда затащили функционал по добавлению/редактирования пользовательских ролей.
Добавьте User setRole и getRole и AuthManager уже не будет казаться таким примитивеным.
И как это относится к избавлению от хранения assignments?
Вам ненужно избавляться от хранения assignments.
Этот метод поможет вам избавится от переопределения класса PhpManager, и исользовать стандактный authManager с произвольными настройками. Пускай разраюотчик решает использовать ему DbManager или PhpManager.
Нет необходимости добавлять User поле role т.к. эта информация будет хранится в Yii::$app->authManager->getRolesByUser($user_id).
Да, обычно так и вывожу через getRolesByUser($id), если использую стандартные менеджеры.
Здесь аналогично разработчик может использовать (или нет) любой свой менеджер, подписавшись на события модели и реализовать такой же assign() в своём обработчике.
Если я, например, использую DbManager и вывожу роли в GridView, то у меня для каждого пользователя на странице GridView будет отдельный запрос чтобы выяснить какие у него есть роли?
Да.
Как всегда отличный материал!!! Спасибо Дима за твой труд!
Отличная статья!
Дмитрий, ошибка у Вас :-)
Исправил.
Пару мелочей по модулю user.
views/frontend/default/passwordReset.php
P.S:. Лень кидать pull request'ы.
Спасибо! Исправил.
Спасибо, отличный материал!
Как привязать роль к не авторизованному гостю? Чтобы в RBAC была роль Гость.
Добавить роль guest и прописать её в defaultRoles.
Дмитрий поясните пожалуйста по Module::t(). Не нашел нигде описания это возможности. В документации только Yii::t()
Отсюда
Спасибо!
Здравствуйте, скажите а есть ли простой способ использовать RBAC вместе с activeDataProvider?
Проверять, что пользователь может читать статью которая выбирается.
А в чём именно проблема?
В данном случае не ясно как это будет выглядеть в принципе.
Мы имеем методы can у user, мы имеем папку с правами RBAC, где лежат правила.
Затем есть контроллер который допустим выводит новости.
Все как мы можем использовать rbac это вызвать Yii::$app->user->can, как при этом заставить activeDataProvider фильтровать данные - не понятно.
Приложение это REST Full API.
Если вы знаете статью на эту тему, буду признателен.
Спасибо)
Для выборок фильтруем хардкорно:
Решил пропустить все, что связано с тестированием, и теперь пожалел... в какой-то момент понял, что у меня не выполняется действие logout модуля user:
в виджете меню все прописано:
А если закомментировать в контроллере:
то logout начинает работать. Зачем вообще отправлять этот Url методом post?
И так и не могу понять, почему перестало работать...
> Зачем вообще отправлять этот Url методом post?
Все что-то производящие операции вроде delete для безопасности (и для защиты от кеширования) делают через POST. А иначе кто-то впишет такую "картинку" или ссылку:
и у администратора этот адрес сработает и в пользователя нечаянно удалит. Аналогично /logout сделано работающим только через POST.
Если вдруг перестало работать после обновления фреймворка, то очистите папку web/assets и обновите страницу.
> Все что-то производящие операции вроде delete для безопасности делают через POST
Спасибо за пояснение)
Очистил папку web/assets (оставил в ней только .gitignore), не помогло...
Заметил, что в модуле admin при нажатии на /logout, действие выполняется.
а в модуле main тот же самый /logout не выполняется.
Описаны они одинаково. пока ищу причину.
Все, решил проблему, дело было в закомментированном фрагменте:
раскомментировал подключение 'yii\web\YiiAsset' и все заработало
а если раскомментировать строку 'yii\bootstrap\BootstrapAsset' то возникает проблема в верскте, конфликты в css файлах
Странно, все сделал как написано
не сохраняются childs и assignments, ни в базе, ни в файлах
отсюда не работает Yii::$app->user->can(AdminRbac::PERMISSION_ADMIN_PANEL
да не читай ты этого дауна, он ноль в программировании
Куда мне нужно копать если не работает команда
Вернее она обрабатывается, в таблицу миграций записывается запись, но вот сами таблицы rbac не добавляются
Весьма странно. С таким не сталкивался.
Sergey Aver В нормальную человеческую БД phpmyadmin и не тратить на этот мусор миграционный ни секунды
Здрвствуйте.
Запускаю команду
валятся ошибки:
Попробовал запустить
- тоже самое.
Видимо где-то в конфиге ошибка закралась?
Где смотреть?
Смотреть в полном тексте ошибки 'Unknown bootstrapping...'.
Извиняюсь, Шторм урезает почему-то.
Вот такой полный текст ошибок:
В секции 'bootstrap' => ['log', 'gii'] в config/console.php удалите 'gii'.
Дмитрий, спасибо.
Удалил.
Теперь при попытке выполнить
при вводе имени пользователя валятся ошибки:
Скорее всего из-за неуказанного в config/components.php:
Ok, указываю.
Но после этого шаг указания роли в консоли зацикливается:
Что я мог упустить?
Роли не создали.
Так ведь роли были созданы в app\commands\RbacController.php
Всего две: user и admin.
Соответственно, ввожу вторым шагом роль admin или user.
В ответ повторный запрос.
И так без конца.
Ничего уже не понимаю.
Где в таком случае необходимо роли создавать?
Тогда бы выводилось:
В общем, создал в корне приложения папку rbac.
В ней файлы assignments, items и rules.
В items прописал роли.
Всё заработало.
В статье не было указано.
Статью изучаю без каких-либо дополнительных знаний.
Буду курить эдишынали.
> В статье не было указано.
После создания в RbacController его нужно запустить.
> Статью изучаю без каких-либо дополнительных знаний.
А смысл?
Действительно, в статье не указано, что после создания RbacController необходимо в рукопашную создать rbac/items.php. Мы, новички, на этом здорово спотыкаемся )
Не в рукопашную создать, а этот контроллер как yii rbac/init запустить.
Я так и запускал, в ответ получал ругательство:
PHP Warning 'yii\base\ErrorException' with message 'Invalid argument supplied for foreach()'
А когда в ручную создал, тогда всё заработало.
Добрый день у меня вылетает ошибка.
Настройте 'authManager' в config/console.php.
Спасибо за ответ но теперь вылетает
Имел ввиду, что изучаю Yii2. И не знал как организовать разграничение прав.
В любом случае Вы даёте полезный материал.
Спасибо.
Подскажите в чем может быть проблема
волшебный коммент :) Написал и понял что в config/console.php нужно прописать новые настройки AuthManager. Спасибо вам. Очень крутые статьи.
у вас на гитхабе есть yii2-hybrid-authmanager. скажите, это усовершенствованный вариант описанного в этой статье Rbac?
можно узнать как он устроен? мультироли как хранятся? json в поле role?
Посмотрите в следующей статье, на которую дана ссылка в конце этой.
Спасибо, отличная статья)
Добрый день!!!
Спасибо за Ваши труды..
я столкнулся с такой проблемой, у нас есть модуль user в нем вид
views/backend/user/login.php
для чего он там? у меня не получается им воспользоваться, мы закрыли модуль админ от не прошеных гостей, а как сделать переброс на вид login.php но уже админ..
Спасибо
Это для использования в advanced-приложении. Если хотите воспользоваться именно им в basic, то в методе beforeAction модуля admin налету замените адрес:
Спасибо такой способ лучше чем мой я делал через denyCallback в access модуля админ....
но результат один и тот же, так как все идет через админ, а админ разрешен только админам:)
то происходит зацикливание...
мне кажется нужно в access rule какое то правило прописать. что бы оно разрешало только логин гостям...
пробовал так..
Спасибо, Дмитрий
вот то что сделал работает
может кому пригодится
Всё здорово! но манера подачи в тексте запутывает в конец.
Идешь последовательно, последовательно ложные примеры, которые в дальнейшем разбиваются о более совершенные. Тут нужен простой копипаст, потом устранять ошибки интерпритатора, текст бесполезен в данном случае, он ничего не раскрывает и не объясняет.
Это статьи для программистов, а не для копипастеров. Увы.
Конечно, я ожидал такой вариант ответа. Для программиста, ценно здесь ваше упорство, с которым вы раскрываете фреймворк. Такое ощущение, что утро вы начинаете с xdebug, а потом чистите зубы.
Просто предлагаю, сосредотачивать статьи на конечном варианте.
Я хочу разобраться как это работает, меня не жмет копипастить, по этому, эта статья не для копипастеров и не для таких как я. Мне нужно увидеть как это работает. В итоге у меня в ide куча вкладок, с гитхаба вкладки, статья. И не понятно, как свести всё воедино.
В итоге я разобрался. Теперь пишу в чем у меня возникли сложности при усвоении материала. Принимать это во внимание или отсылать меня лесом, дело ваше. Вам все равно спасибо, за труды! Эти статьи хорошие примеры, к ним претензий нет. Суть моего коммента описана выше. Я поймал это исключение при чтении, практически всего цикла статей по yii2. Еще раз спасибо, подписан давно, жду следующих открытий.
Анатолий Белов Согласен полностью, писать статьи уже давно разучились все, но в топы все лезут и лезут.. Жаль гугл и яндекс на столько конченые, что забили уже на проверку сайтов
Эти статьи для долбаебов вроде тебя, уебок очкастый. Удали свой дерьмосайт и не засоряй инет своим гавнокодом! Тебе уже кучу людей сказало, что ты даун, ты ноль в программировании!
Запомни дятел, для программистов не нужны статьи, они и так все знают. Создай закрытое сообщество, раздай всем своим ссылки и сидите там, а не засоряйте собой поисковики! Реально заебали!
Никто блять не может по человечески обьяснять, лишь бы херь всякую написать и выложить! Только путаете всех, кто начинает разбираться. Как будто сами небыли на нашем месте! Очистить бы вам мозг от всего этого, и дать ваши статьи. Заебетесь разбираться.
В топах ещё и другие статьи есть кроме моих. Так что в путь! :)
В топах есть такой же мусор гавнокодера! Так что в путь с названием "нахуй из инета" вместе со своими статьми
Ну раз кругом один мусор, то лыжи точно не едут. Регистрацию вы из-за меня за этот год не осилите. Печаль-беда.
Мои статьи все про процесс, а не про вам так сразу нужный результат. Просто скопипастить у меня вам ничего не получится. А нужно объяснение каждой вещи в примерах - смотрите другие статьи, документацию, исходники, Development Cookbook.
Добрый день.
При выполнении php yii roles/assign
выдает ошибку
Переделайте User в ActiveRecord.
Дмитрий, приветствую!
Я столкнулся с такой проблемой - при добавлении пермишенов для новых контроллеров в метод init, я в самом начале делаю
Затем все пермишены пересоздаются, новые добавляются, но в таблице auth_assignment удаляются все записи и соответственно уже созданные пользователи с ролями теряют все свои возможности.
Как быть в такой ситуации? Неужели каждый раз нужно будет добавлять новый метод для новых пермишенов?
Может быть можно как-то делать бэкап текущих назначенных прав на лету?
Заранее спасибо за ответ!
Либо бэкапить и восстанавливать в init, либо добавлять роли через миграции.
Вдруг кому пригодится
очередная дерьмостатья ничего не объясняющая... где все перепутано.... гавнокодер как всегда
Вам это так не нравится, что уже кучу статей прочитали? Да вы мазохист :)
в статье куча бесполезного кода: вроде вызова array_filter() при выводе Nav::widget() или класса Rbac - и так есть псевдонимы "@", "?".
> вроде вызова array_filter() при выводе Nav::widget()
Это полезный код, очищающий список от пустых пунктов.
> или класса Rbac - и так есть псевдонимы "@", "?"
Это класс для констант с именами разрешений. Псевдонимами разрешния не заменить.
добавлять в список пустые значения чтобы потом их отфильтровывать это такое себе решение.
> добавлять в список пустые значения чтобы потом их отфильтровывать это такое себе решение.
Некоторые пункты отключаются тернарными операторами. Предлагаете переписать на простыню if-ов?
> откуда в RBAC им взяться?
Оттуда же, откуда именам ролей.
я видимо не правильно выражаюсь - попробуем по другому: в RBAC Вы раздаете разрешения с помощью ролей, роль включает в себя множество разрешений. У пользователя может и должно быть несколько ролей. поэтому для доступа в админку достаточно роли admin/@, но для того чтобы добавить или отредактировать свой контент нужна роль (content) manager, для изменения чужого контента нужна роль moderator.
Суть как раз в переходе от проверки ролей на проверку разрешений.
Например, для модерирования комментариев можно было во всех условиях ориентироваться на роли:
Но если вдруг захотим добавить ещё одну роль support службы поддержки или выдать доступ к комментариям менеджеру, то придётся каждый раз эти if-ы по всему коду менять.
Но вместо этого удобнее сделать всего одно разрешение manageComments для доступа к комментариям:
И теперь можем создавать сколько угодно ролей, проставляя в конфигурации доступные им разрешения:
Теперь чтобы добавить новую роль просто создаём её в конфигурации:
без необходимости менять if-ы.
Роли все плоские, друг от друга никак не наследуются. Иначе бы мы заблудились в деревьях наследования.
Можно даже дать администратору возможность создавать новые роли в админке и проставлять галочками разрешения для них.
> У пользователя может и должно быть несколько ролей
При такой системе разрешений пользователю обычно достаточно одной роли. Если же какому-то пользователю нужен индивидуальный подход, то можно сделать новую роль лично для него.
Поймите уже наконец, RBAC - это довольно простая система управления доступом, нужна сложнее? используйте другую модель.
Один субъект может иметь несколько ролей.
Одну роль могут иметь несколько субъектов.
Одна роль может иметь несколько разрешений.
Одно разрешение может принадлежать нескольким ролям.
Роли назначаются субъектам, вследствие чего субъекты получают те или иные разрешения через роли. RBAC требует именно такого назначения, а не прямого - назначение разрешений субъектам, иначе это приводит к сложно контролируемым отношениям между субъектами и разрешениями.
Ну так эту модель RBAC мы как раз и используем. Субъекту назначаем роль. Роль имеет несколько разрешений. Именно так через роль разрешения и назначаем, а не напрямую субъекту.
Так что не так?
"admin" - роль, "adminPanel" - ресурс.
"admin" - роль. "allowAdminPanel" - разрешение.
Исправил описку в предыдущем комменте: "adminPanel" - ресурс.
меня смущает подобный код:
для просмотра разделов админки достаточно роли admin, изменение своих записей - manager и изменение любых записей - moderator
Контент-менеджеру и модератору тоже нужна админка, чтобы там править контент и модерировать комментарии.
RBAC не подразумевает явного наследования ролей как это сделано у Вас. Для доступа на чтение админки у пользователя должна быть роль admin, но при этом он может иметь и роли manager/moderator позволяющие ему не только просматривать контент, но и редактировать.
> Но вместо этого удобнее сделать всего одно разрешение manageComments для доступа к комментариям
все верно, это разрешение которое может быть у нескольких ролей, эту часть я никоим образом не оспариваю.
А в Yii псевдоним "admin/@" так не работает.
Вы не знаете что слеш применяется в качестве разделителя? а & - это "и"))
Ссылку на это можно?
Wikipedia подойдет?
(Our New Zealand / Western Australia trip — наше путешествие по Новой Зеландии / Западной Австралии)
Какое отношение это имеет к фреймворку Yii?
А так да, если ваш код умеет работать с масками, то используйте "admin/users" для конкретных разрешений, а в админку пускайте по "admin/@". Тогда отдельного разрешения "admin/panel" не понадобится.
"@" - alias "admin", подразумевалось admin(@), часто вместо скобок ставится слеш - на дешевых печатных машинках не было кнопок со скобками.
Дмитрий, здравствуйте!
Как ограничить доступ на уровне контроллера/метода понятно. Но как ограничить на уровне модуля, разделенного на frontend и backend? Причем в обеих частях сайта могут быть контроллеры с одинаковым именем.
Как ограничить на уровне родительского модуля admin?
Как вариант, можно в конфигурации:
Yii сам в этом плане не очень продуман.
после констант, стало все ясно... автор не понимает вообще ничего в этом....... костыли гавнокодеров
Поясните.
Удивительно читать подобное. Опытные программисты либо аргументами давят либо игнорируют.
в файле modules/user/Module.php в методе behaviors прописал прописал правило доступа к контроллерам модуля, но перестали работать консольные контроллеры этого модуля.
Выдает ошибку
Ставлю в правила только
и тоже самое, но если удалить поведение, то все нормально.
В чем может быть дело? Или AccessControl не совместим с консольными контроллерами?
Просто AсcessControl лезет в Yii::$app->user, которого в консоли нет. Yii сам в этом плане не очень продуман.
Как вариант, можно навешивать правила на модуль в конфигурации:
вместо навешивания в behaviors() модуля. Тогда не будет путаницы прав фронтенда, бекенда и консоли.
Скачал ваше расширение для гибридного RBAC на GitHub так вот в классе AuthManager есть свойство
public $modelClass = 'app\models\User'; И это свойство делает доступным ваше расширении только для basic шаблона. Приходится переписывать его под себя под advanced
Если есть желание поправьте
Все такие настройки в Yii переопределяются в конфигурации:
Уже сам догадался))) спасибо
Спасибо за статью!
Вот этот фрагмент поправьте, пожалуйста:
"для простоты наследуем admin от user" -> "для простоты наследуем user от admin"
Вы смешиваете/путаете RBAC и ACL. Зачем создавать псевдо роль adminPanel? Разве наличие у пользователя роли admin не гарантирует ему доступ в админку?
В тексте куча дублирования:
> Для нашего случая нужны всего две роли user и admin. Можно создать только их и проверять доступ по этим ролям. Но более гибкий и предпочтительный вариант – это использование так называемых «разрешений» вроде adminPanel и подобных и привязка их к ролям.
и прямо за этим повторяете тоже самое:
> Использование имён разрешений вроде adminPanel в модулях предпочтительнее имён ролей вроде user или admin, так как на каждом сайте можно будет делать разных админов и пользователей, в любой последовательности привязывая к ним ваши разрешения.
> Вы смешиваете/путаете RBAC и ACL.
RBAC удобен для общего разграничения по ролям и разрешениям для всех элементов. ACL - для индивидуального контроля по правилам для конкретного элемента. В чём именно путаница?
> Зачем создавать псевдо роль adminPanel?
Ответил как раз в первом повторе, что "более гибкий и предпочтительный вариант – это использование так называемых «разрешений» вроде adminPanel и подобных и привязка их к ролям". Это мы и делаем.
> Разве наличие у пользователя роли admin не гарантирует ему доступ в админку?
На это ответил во втором повторе, что "на каждом сайте можно будет делать разных админов и пользователей, в любой последовательности привязывая к ним разрешения". Админу эта роль доступ сгарантирует, а контент-менеджеру или модератору - нет.
> RBAC - это довольно простая система прав. Вы же пытаетесь выдать Ваш микс RBAC + ACL за RBAC, но adminPanel - это не роль, а метка ресурса.
Моё adminPanel - это не роль или метка ресурса, а разрешение.
> нет в RBAC никаких доп.разрешений, только доступ по ролям.
Не знаю как у вас, а у нас в RBAC всё есть.