Сервис на 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')) ?>
 
    ...
 
    <?php 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/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

Не забудьте подписаться на рассылку статей в сайдбаре или вместе с нами записаться и «позажигать» на бесплатных вебинарах. До встречи!

Комментарии

 

LAV45
class AuthManager extends PhpManager { ... }

> Но такой менеджер слишком примитивен.

Это не менеджер, а вы просто не туда затащили функционал по добавлению/редактирования пользовательских ролей.

Добавьте User setRole и getRole и AuthManager уже не будет казаться таким примитивеным.

class User extends ActiveRecord implements IdentityInterface
{
...
    public function setRole($name)
    {
        $auth = Yii::$app->authManager;

        if (!empty($name)) {
            $userRoles = array_keys($auth->getRolesByUser($this->id));
            if (!isset($userRoles[0]) || $userRoles[0] != $name) {
                $role = $auth->getRole($name);
                $event = $this->getIsNewRecord() ? self::EVENT_AFTER_INSERT : self::EVENT_AFTER_UPDATE;

                $this->on($event, function () use ($auth, $role) {
                    $auth->revokeAll($this->id);
                    $auth->assign($role, $this->id);
                });
            }
        } elseif ($this->getIsNewRecord() === false) {
            $auth->revokeAll($this->id);
        }
    }

    public function getRole()
    {
        $auth = Yii::$app->authManager;
        $roles = $auth->getRolesByUser($this->id);
        return !empty($roles) ? array_keys($roles)[0] : null;
    }

    public static function getRoleList()
    {
        $data = Yii::$app->authManager->getRoles();
        $roles = ArrayHelper::getColumn($data, 'description');
        return $roles;
    }
}
Ответить

 

Дмитрий Елисеев

И как это относится к избавлению от хранения assignments?

Ответить

 

lav45

Вам ненужно избавляться от хранения assignments.
Этот метод поможет вам избавится от переопределения класса PhpManager, и исользовать стандактный authManager с произвольными настройками. Пускай разраюотчик решает использовать ему DbManager или PhpManager.
Нет необходимости добавлять User поле role т.к. эта информация будет хранится в Yii::$app->authManager->getRolesByUser($user_id).

Ответить

 

Дмитрий Елисеев

Да, обычно так и вывожу через getRolesByUser($id), если использую стандартные менеджеры.

Здесь аналогично разработчик может использовать (или нет) любой свой менеджер, подписавшись на события модели и реализовать такой же assign() в своём обработчике.

Ответить

 

Гераклит Вяткин

Если я, например, использую DbManager и вывожу роли в GridView, то у меня для каждого пользователя на странице GridView будет отдельный запрос чтобы выяснить какие у него есть роли?

Ответить

 

Дмитрий Елисеев

Да.

Ответить

 

Дмитрий Кривошеин

Как всегда отличный материал!!! Спасибо Дима за твой труд!

Ответить

 

Andrey

Отличная статья!

Ответить

 

Олег

Дмитрий, ошибка у Вас :-)

$ yii roles/assign
Username: admin
Role: [user,admin,?]: admin
Error: Getting unknown property: yii\console\Application::user
Ответить

 

Дмитрий Елисеев

Исправил.

Ответить

 

Олег

Пару мелочей по модулю user.

'BUTTON_SEND' => 'Send',
'BUTTON_SEND' => 'Отправить',

views/frontend/default/passwordReset.php

<?= Html::submitButton(Module::t('module', 'BUTTON_SAVE'), ['class' => 'btn btn-primary', 'name' => 'reset-button']) ?>

P.S:. Лень кидать pull request'ы.

Ответить

 

Дмитрий Елисеев

Спасибо! Исправил.

Ответить

 

...

Спасибо, отличный материал!

Ответить

 

Николай Куропятников

Как привязать роль к не авторизованному гостю? Чтобы в RBAC была роль Гость.

Ответить

 

Дмитрий Елисеев

Добавить роль guest и прописать её в defaultRoles.

Ответить

 

Anton Gubarev

Дмитрий поясните пожалуйста по Module::t(). Не нашел нигде описания это возможности. В документации только Yii::t()

Ответить

 

Дмитрий Елисеев
Ответить

 

Anton Gubarev

Спасибо!

Ответить

 

Михаил

Здравствуйте, скажите а есть ли простой способ использовать RBAC вместе с activeDataProvider?
Проверять, что пользователь может читать статью которая выбирается.

Ответить

 

Дмитрий Елисеев

А в чём именно проблема?

Ответить

 

Михаил

В данном случае не ясно как это будет выглядеть в принципе.
Мы имеем методы can у user, мы имеем папку с правами RBAC, где лежат правила.
Затем есть контроллер который допустим выводит новости.
Все как мы можем использовать rbac это вызвать Yii::$app->user->can, как при этом заставить activeDataProvider фильтровать данные - не понятно.

Приложение это REST Full API.
Если вы знаете статью на эту тему, буду признателен.
Спасибо)

Ответить

 

Дмитрий Елисеев

Для выборок фильтруем хардкорно:

Post::find()->andWhere(['user_id' => Yii::$app->user->id])
Ответить

 

Иван Зайцев

Решил пропустить все, что связано с тестированием, и теперь пожалел... в какой-то момент понял, что у меня не выполняется действие logout модуля user:

Method Not Allowed. This url can only handle the following request methods: POST.

в виджете меню все прописано:

'url' => ['/user/default/logout'],
'linkOptions' => ['data-method' => 'post']]

А если закомментировать в контроллере:

'actions' => [
    // 'logout' => ['post'],
],

то logout начинает работать. Зачем вообще отправлять этот Url методом post?
И так и не могу понять, почему перестало работать...

Ответить

 

Дмитрий Елисеев

> Зачем вообще отправлять этот Url методом post?

Все что-то производящие операции вроде delete для безопасности (и для защиты от кеширования) делают через POST. А иначе кто-то впишет такую "картинку" или ссылку:

<img src="/admin/user/delete/1" />
<a href="/admin/user/delete/1">здесь</a>

и у администратора этот адрес сработает и в пользователя нечаянно удалит. Аналогично /logout сделано работающим только через POST.

Если вдруг перестало работать после обновления фреймворка, то очистите папку web/assets и обновите страницу.

Ответить

 

Иван Зайцев

> Все что-то производящие операции вроде delete для безопасности делают через POST
Спасибо за пояснение)

Очистил папку web/assets (оставил в ней только .gitignore), не помогло...
Заметил, что в модуле admin при нажатии на /logout, действие выполняется.
а в модуле main тот же самый /logout не выполняется.
Описаны они одинаково. пока ищу причину.

Ответить

 

Иван Зайцев

Все, решил проблему, дело было в закомментированном фрагменте:

public $depends = [
    //'yii\web\YiiAsset',
    //'yii\bootstrap\BootstrapAsset',
];

раскомментировал подключение 'yii\web\YiiAsset' и все заработало
а если раскомментировать строку 'yii\bootstrap\BootstrapAsset' то возникает проблема в верскте, конфликты в css файлах

Ответить

 

SerF SerF

Странно, все сделал как написано
не сохраняются childs и assignments, ни в базе, ни в файлах
отсюда не работает Yii::$app->user->can(AdminRbac::PERMISSION_ADMIN_PANEL

Ответить

Оставить комментарий

Войти | Завести аккаунт


(никто не увидит)



Можно использовать теги <p> <ul> <li> <b> <i> <a> <pre>