doniys_a
@doniys_a
Backend-разработчик (Php, node.js, python, ruby)

Как организовать RBAC в Yii2 для контроля помимо доступов еще и доступ по языку к контенту?

Суть заключается вот в чем. Имеется мультиязычный сайт.
Необходимо реализовать разграничение прав доступа к контенту для разных стран.
К примеру, когда менеджер заходит на испанский, редактировать может контент только на испанском, но должны так же присутствовать возможности управления доступными для менеджера языками. Т.е. нужно предусмотреть возможность тому же менеджеру добавить еще один язык или несколько (при необходимости), которые он может редактировать. Именно конкретному пользователю, а не группе.

Мое видение решение проблемы заключается в расширении rbac
Создаю еще одну таблицу rbac_language которая содержит список доступных языков
и rbac_language_assignment, где конкретный язык хранится.
Но вот в чем вопрос. В таком варианте очень неудобно использовать permission для разграничения прав, так как permission можно привязать только к группе пользователя, но не к самому пользователю.
И тут возникает проблема такой архитектуры - у пользователя появляется много групп.
С другой стороны, заводить каждый раз новую группу пользователя для нового набора доступных языков будет ли лучшим вариантом решения задачи или есть решения лучше?

Спрашиваю с целью получить советы или критику проектирования разграничений прав доступа и предложения по улучшению.
  • Вопрос задан
  • 686 просмотров
Пригласить эксперта
Ответы на вопрос 2
mhthnz
@mhthnz
PHP, YII2, Golang, Linux
В таком варианте очень неудобно использовать permission для разграничения прав, так как permission можно привязать только к группе пользователя, но не к самому пользователю.

Странно, у меня все получалось:
$authManager = Yii::$app->authManager;
$userID = 1;
$permission = $authManager->getPermission('language_ru');
$authManager->assign($permission, $userID);

Таким образом пользователю можно сколь угодно прав выдать.
Ответ написан
doniys_a
@doniys_a Автор вопроса
Backend-разработчик (Php, node.js, python, ruby)
Да, это так, но есть одно "но", пытаюсь автоматизировать проверку правил, из замечательного проекта https://github.com/trntv/yii2-starter-kit взят GlobalAccessBehavior, который задает глобально права доступы на экшины.
Вот как это выглядит
Есть консольная команда, которая пробегается по всем модулям админки, собирает контроллеры и все экшины, так же пробегается по контроллерам внутри админки и делает то же самое.
правила составляются следующим образом
backend___
Далее это все привязывается к роли.
Все работает отлично, но когда возникает ситуация, когда конкретному пользователю (а их может быть и десяток) нужно отключить определенный контроллер или даже экшн, то в случае с использованием permission, его не понимает AccessControl, так как доступ задать можно для роли.
В случае использования permission, подобную проверку проводить нужно в методе контроллера beforeAction
public function beforeAction() {
        $permission = 'backend_' . Yii::$app->controller->module->id . '_' . Yii::$app->controller->id . '_' . Yii::$app->controller->action->id;

        if (!Yii::$app->user->can($permission)) {
            throw new yii\web\ForbiddenHttpException;
        }
    }

В таком случае, это необходимо реализовывать в базовом контроллере.
Еще есть вариант все же заставить понимать AccessRule permission а не только роли, немного его переопределив
<?php

namespace common\behaviors;
use yii\base\Action;
use yii\filters\AccessRule;
use yii\helpers\Inflector;


/**
 * @class   AccessRuleCustom
 *
 * @package common\behaviors
 * @author  Sergey Doniy <doniysa@gmail.com>
 */
class AccessRuleCustom extends AccessRule
{
    public $checkPermissions = true;
    public $prefix = 'backend_';

    public function allows($action, $user, $request)
    {
        return parent::allows($action, $user, $request) || $this->hasPermission($action);
    }

    protected function hasPermission(Action $action) {
        if (!$this->checkPermissions)
            return false;

        $permission = $this->prefix . $action->controller->module->id . '_' . $action->controller->id . '_' . Inflector::id2camel($action->id);

        return \Yii::$app->user->can($permission);

    }

}

Во втором варианте все работает, во всяком случае permission проходят, и все ок. Остановился в конечном счете на этом варианте - у пользователя всего одна роль, к которой привязываются дефолтные пермишины, а отдельные привязываются непосредственно к пользователю.
Для того, чтобы не хардкодить с языками и ролями по ним, отдельно создал две таблицы
auth_assignment_language_item
которая хранит перечень языков - т.е. по сути permission для языка
и таблицу
auth_assignment_language
Которая в свою очередь хранит привязку отдельного пользователя к языку
и таблицу auth_assignment_language_child
Для привязки роли к языку
Расширен функционал RBAC для управления еще и языками (так как rbac храню в БД, то расширен DbManager)
В отдельные таблицы вынес для того, чтобы логика данных была прозрачна, что позволило достаточно быстро накидать gui-интерфейс для управления правами на конкретный язык.
Это я описал свою реализацию поднятой темы.
Но все равно не уверен, что нет более лучших подходов для реализации.
Интересно будет почитать, кто как решал подобные задачи и какими методами - сразу уточню - система пишется не под один проект, это внутренняя CMS компании и hot-фиксы и пинание системы под конкретный случай усложнит разработку следующих проектов, поэтому приходится по максимуму (насколько позволяет время) делать ее гибкой и расширяемой для большинства задач, которые перед разработчиками ставятся.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы