Пользователь
0,0
рейтинг
24 апреля 2013 в 23:01

Разработка → RBAC Авторизация в YII и LDAP tutorial

Yii*

RBAC — это простой и мощный способ централизованного управления доступом в веб приложении. Основным его достоинством является то, что при правильном понимании и применении иерархии авторизации можно очень гибко управлять доступом не изменяя код контроллеров.

К сожалению стандартный мануал по RBAC в YII оставляет больше вопросов чем ответов. Эту ситуацию я и намереваюсь исправить.
Я расскажу о создании “правильной” иерархии: как делать не стоит. А в завершении я приберёг инструкцию, о том как подружить LDAP авторизацию (из ActiveDirectory ) с Yii и RBAC.

Все кто заинтересовался, добро пожаловать под кат!


RBAC (Role Based Access Control) Система доступа на основе ролей. В основу этой системы в Yii ложатся три основных звена:
  • Роль (Role)
  • Задача (Task)
  • Операция (Operaton)

Я предполагаю, что читатель уже вкратце ознакомился со страницей официального учебника YII и знает основные принципы механизма авторизации в Yii.

Поэтому перейдем сразу к построению правильной иерархии элементов авторизации.

Иерархия ролей.


Самое важное, и при этом самое запутанное для понимания — это иерархия элементов в RBAC. От того насколько правильно она продуманна зависит то, как гибко вы сможете управлять ролями в системе, и как часто вам придется изменять код контроллеров.

Рассмотрим подробнее каждый элемент авторизации:
  • Операция — это низший элемент авторизации. Именно его мы должны проверять в коде наших контроллеров. Другими словами: операция — это то, что цепляется к коду.
  • Роль — это наоборот высший элемент авторизации, группирующий в себе операции и задачи. И именно роль мы должны прикреплять к пользователям.
  • Задача — Это необязательный элемент, находящийся между операцией и ролью, и сужающий права операции с помощью bizRule. Что бы облегчить понимание, назовем ее фильтром.



На диаграмме выше указана типовая иерархия, где контроллеры проверяют операции, а к пользователям привязываются роли. Однако YII не запрещает вам проверять в контроллере что либо другое, например, роль пользователя.
Но вы должны помнить, что это НЕПРАВИЛЬНО и приводит к тому, что вы лишаетесь преимуществ централизованного управления.

Рассмотрим пример:
У нас есть Новости, к управлению которыми мы хотим разграничить доступ.
Первое, что мы должны сделать при проектировании RBAC это продумать возможные ОПЕРАЦИИ (а не роли пользователей, как кажется на первый взгляд).

Обычно новости можно удалять, создавать, читать и редактировать. Преобразуем эти действия в операции: deleteNews, createNews, readNews, updateNews.

В коде вы можете проверять любую из этих операций:

if(Yii::app()->user->checkAccess('createNews'))
{
    // создаём новость
}

//или 

if(Yii::app()->user->checkAccess('updateNews'))
{
    // обновляем новость
}

После того как продуманны операции, можно переходить к ролям (я намеренно пропускаю задачи, о них мы поговорим чуть позже):

Из имеющихся операций мы можем выделить следующие роли:
newsReader, newsManager, newsAuthor.

Иерархия элементов при этом будет такой:
  • newsReader
    • readNews
  • newsAuthor
    • readNews
    • createNews
  • newsManager
    • readNews
    • createNews
    • deleteNews
    • updateNews

Эти роли можно прикреплять к конкретным пользователям. Но лучше создать еще одну абстракцию ролей более обобщенную, и прикреплять к пользователям её, например:

  • guest
    • newsReader
  • authorized
    • newsAuthor
  • moderator
    • newsManager

Такая абстракция удобна, если у нас предстоит управление не только новостями, но еще, скажем, картинками в фотогалерее или товарами в магазине. Тогда для каждого такого раздела вашей системы необходимо создать свои “промежуточные” роли, например photoReader (showPhoto), photographer(showPhoto, addPhoto), photoManager (showPhoto, addPhoto, deletePhoto) и прикрепить их к обобщенным ролям:

  • guest
    • newsReader
    • photoReader
  • authorized
    • newsAuthor
    • photographer
  • moderator
    • newsManager
    • photoManager

Т.е. это можно прочитать как: гость может читать новости и смотреть фото; авторизованный пользователь может писать новости и добавлять фотографии; модератор может делать всё вышеперечисленное, а так же редактировать и удалять чужие фото и новости.

Вы наверняка заметили, что у ролей newsAuthor и photographer недоступна операция update. Правильно, потому что если на данном этапе мы дадим им операции updateNews или updatePhoto, то они смогут управлять всеми фото без разбора. А нам необходимо, что бы авторы могли отредактировать только свои элементы.

Именно для этого и созданы задачи. Задачи — это фильтры, которые могут уточнять права. Создадим задачу updateOwnNews, потомком которой назначим updateNews. Из названия задачи понятно, что она будет позволять редактировать собственные новости, и поможет нам в этом bizRule.

bizRule — это некий PHP код, результатом выполнения которого должен быть ответ: применять ли данное правило к этому юзеру или нет.

bizRule для нашей задачи updateOwnNews будет выглядеть следующим образом:
$bizRule='return Yii::app()->user->id==$params["news"]->authID;';

Где мы проверяем совпадает ли id автора новости с текущим авторизованным юзером.

Что бы получить объект текущей новости в бизнес правиле, его прежде необходимо туда передать:

$params=array('news'=>$post);
if(Yii::app()->user->checkAccess('updateNews',$params))
{
    // обновляем запись
}

Обратите внимание, мы проверяем не конкретную задачу (updateOwnNews), а операцию updateNews(самый низший элемент иерархии).

Благодаря иерархии, которая после создания задачи updateOwnNews стала такой:

  • newsAuthor
    • readNews
    • createNews
    • updateOwnNews
      • updateNews

Yii начнет проверку доступа с самого низа и пойдет по иерархии вверх, т.е. проверит updateNews, затем перейдет к updateOwnNews. На каждом этапе проверки Yii будет проверять: задано ли правило bizRule, и если задано, то будет передавать в него параметры, указанные в функции checkAccess.

Схематично проверку можно изобразить так:



На схеме изображено 3 сценария проверки:
Первый сценарий, это когда авторизованный пользователь пытается отредактировать свою новость. В этом случае проверка поднимаясь снизу вверху проходит через updateOwnNews. И поскольку идентификаторы пользователя совпадают — завершается успехом.

Во втором случае пользователь имеет роль модератора. В его иерархии задачи updateOwnNews нет, поэтому проверяется только наличие операции updateNews.
Проверка проходит успешно.

В третьем случае авторизованный пользователь пытается отредактировать чужую статью, и на этапе updateOwnNews проверка завершается отказом, т.к. не удовлетворено bizRule задачи.

Примеры указанные выше демонстрируют централизованность управления правами.
Написав один раз в контроллере проверку на выполнение операции и передав туда параметр, вся дальнейшая работа по разграничению доступа ложится на плечи RBAC.
Поэтому по возможности, вы должны всегда передавать в функцию checkAccess параметры (даже если проверяемый элемент не имеет bizRule), и проверять операцию, а не роль.

Если в контроллере в одном условии вы хотите написать более одной проверки — знайте, вы идете в неверном направлении — у вас проблемы с организацией иерархии.

К примеру:
if(Yii:app()->user->checkAccess('moderator') && Yii:app()->user->checkAccess('administrator')) {
	//delete smth
}

Это НЕправильно. При таком подходе вы не сможете централизованно управлять правами. Вам придется каждый раз редактировать код и добавлять сюда новое условие.

Способы проверки прав в контроллерах


Всего имеется 2 способа проверки прав.
Первый способ мы уже рассмотрели. Это метод checkAccess() компонента CWebUser.

Но для тех кто заботится о том, что бы его контроллеры не “толстели”, есть еще один аспектно-ориентированный способ проверки прав.

Способ заключается в подключении к контроллеру фильтра 'accessControl'.

Этот фильтр сделает за вас всю грязную работу: он проверит наличие прав доступа и, при необходимости, отправит пользователя на страницу 403. Таким образом вам не придется дублировать код проверок в каждом экшене.

Рассмотрим работу фильтра на примере контроллера новостей:

class NewsController extends CController
{
    …
    public function filters()
    {
        return array(
            'accessControl',
        );
    }

   public function accessRules()
    {
        return array(
            array('allow',
                'actions'=>array('create'),
                'roles'=>array('createNews'),
            ),
            array('allow',
                'actions'=>array('delete'),
                'roles'=>array('deleteNews'),
            ),
            array('allow',
                'actions'=>array('view'),
                'roles'=>array('readNews'),
            ),
            array('allow',
                'actions'=>array('update'),
                'roles'=>array('updateNews'),
            ),
         );
    }
 ...
}

В функции accessRules мы указываем 4 правила, каждое из которых является массивом. Где ключ actions указывает для каких Action’ов мы применяем правило, а ключ roles для каких ролей.

Следует отметить, что несмотря на то, что ключ называется 'roles', вписывать туда можно любой элемент авторизации, будь то операция или задача. Но как мы с вами знаем, в контроллерах мы должны проверять только операции, поэтому именно они и вписаны в примере выше.

Данный фильтр помогает избавиться нам от многих строк “сквозного” кода в экшенах. Однако есть проблема: нам необходимо в 'updateNews' передать текущую новость, что бы bizRule определенные в updateOwnNews корректно отработали.

Что бы понять, как можно передавать параметры в фильтр, пришлось залезть в код фрэймворка и подсмотреть там. К счастью с версии 1.1.11 такая возможность появилась.

Что бы передать параметры, необходимо написать правило вот так:

'roles'=>array('newsUpdate'=>array('news'=>$news))

Однако проблемы на этом не заканчиваются. Фильтр запускается ДО любого экшена, а значит мы еще не создали объект новости, который могли бы передать.

Решением может быть следующий подход:
protected $model;

    public function accessRules() {
        return array(
            ...
            array('allow',
                'actions' => array('update'),
                'roles' => array(
                    'updateNews' => array(
                        'news' => $this->news
                )),
            ),
            ...
            );
    }

    public function getNews() {
        if ($this->actionParams['id']) {
            return $this->loadModel($this->actionParams['id']);
        }
    }

    public function loadModel($id) {
      if ($this->model === null) 
        $this->model = News::model()->findByPk($id);

        if ($this->model === null)
            throw new CHttpException(404, 'The requested page does not exist.');
        return $this->model;
    }

Здесь я создаю геттер для поля news, в котором получаю модель новости, через функцию loadModel. Но что бы не дергать базу по несколько раз (в начале при проверке прав, а затем в самом действии), я создал закрытое поле $model, в которое закешировал модель.И при следующем обращении к функции loadModel, модель будет получена из свойства, а не из базы.

К сожалению этот способ не подходит, если в правило нужно отправить параметры, для которых требуется более сложная логика. Поэтому для таких случаев остается использовать checkAccess();

RBAC Yii и LDAP


LDAP это Lightweight Directory Access Protocol — «облегчённый протокол доступа к каталогам» — говорит нам Wikipedia. В нашем случае мы будем получать доступ к каталогу ActiveDirectory, с целью авторизации пользователей из корпоративной сети по своему логину и паролю.

PHP имеет встроенную поддержку LDAP, и поэтому изобретать ничего не придется, к тому же есть много готовых компонентов предоставляющий удобный интерфейс для доступа к каталогам.

Я использовал компонент adLdap, т.к. он заточен именно под ActiveDirectory, предоставляет простой и удобный ООП API и с ним просто приятно работать.

Для начала я подключил adLdap к Yii, как компонент приложения, т.е.:
//protected/config/main.php
‘components’ => array(
	...
	'ldap' => array(
            'class' => 'LdapComponent',
            'baseDn' => 'DC=example,DC=org', //example.org
            'accountSuffix' => '@example.org',
            'domainControllers' => array('dc.example.org'),
            'adminUsername' => 'username',
            'adminPassword' => 'password'
        ),
	..
)

Сам класс LdapComponent:

//protected/components/LdapComponent.php
Yii::import('application.vendors.adLDAP.adLDAP');

class LdapComponent extends adLDAP {

    public $baseDn;
    public $accountSuffix;
    public $domainControllers;
    public $adminUsername;
    public $adminPassword;

    public function __construct() {

    }

    public function init() {
        parent::__construct();
    }
}

Конфигурирование adLdap производится путем переопределение его свойств. Мне захотелось вынести настройки этого компонента в конфиг в привычном для Yii-программиста формате.Поэтому я переопределил нужные поля, изменив их атрибут видимости (что бы Yii мог сконфигурировать компонент) и перенес конструктор в метод init(), что бы конструктор вызывался ПОСЛЕ того как объект будет сконфигурирован (поля заполнены).

Далее мы можем просто использовать этот компонент как все другие в Yii:
Yii::app()->ldap

Для авторизация с помощью LDAP, необходимо создать стандартные компоненты, необходимые для авторизации в Yii: UserIdentity и WebUser:

//protected/components/LdapIdentity.php 
class LdapIdentity extends CUserIdentity
 {

    protected $_id;
    
    /**
     * Authenticates a user via LDAP.
     * @return boolean whether authentication succeeds.
     */
    public function authenticate() {

        $ldap = Yii::app()->ldap;

        $result = $ldap->authenticate($this->username, $this->password);
        $ldapUserInfo = $ldap->user()->infoCollection($this->username, array("mail", "displayname"));

        $this->setState('fullname', $ldapUserInfo->displayname);
        $this->setState('email', $ldapUserInfo->mail);

        if (!$result) {
            $this->errorCode = self::ERROR_USERNAME_INVALID;
        } else {
            $dbUser = User::model()->findByAttributes(array('ldap' => $this->username));

            if (!$dbUser) {
                $dbUser = new User();
                $dbUser->ldap = $this->username;
                $dbUser->save();
            }
            
            $this->_id = $dbUser->primaryKey;

            $this->errorCode = self::ERROR_NONE;
        }


        return !$this->errorCode;
    }

    public function getId() {
       return $this->_id;
    }
    
}

В коде выше мы переопределяем метод authenticate класса CUserIdentity, для того что бы реализовать свою логику авторизации. Мы пытаемся авторизовать этого юзера в AD через adLdap, и если успешно, то заносим его имя и email в постоянное хранилище.

Я решил помимо LDAP вести дополнительную информацию о пользователях в БД, поэтому после успешной авторизации, проверяется: есть ли уже на этого пользователя строка в базе, и если нет- то она создается.

//protected/components/LdapUser.php
class LdapUser extends CWebUser {

    protected $_groups = null;
    protected $_model;

    /**
     * 
     * @return type
     */
    public function getGroups() {
        if ($this->_groups === null) {
            if ($user = $this->getModel()) {
                $this->_groups = Yii::app()->ldap->user()->groups($user->ldap);
            }
        }
        return $this->_groups;
    }

    /**
     * 
     * @return User
     */
    public function getModel() {
        if (!$this->isGuest && $this->_model === null) {
            $this->_model = User::model()->findByPk($this->id);
        }
        return $this->_model;
    }

}

Класс LdapUser почти не отличается от стандартного, кроме важной для нас функции LdapUser::getGroups(). Эта функция, как не сложно догадаться, возвращает из AD все группы этого пользователя.

Я решил сделать группы пользователей в ActiveDirectory ролями в приложении Yii.
Т.е. роли мы будем назначать не конкретным пользователям, а группам. А какую группу кому присвоить будет управляться централизованно через AD.
Это очень удобно в корпоративных порталах и прочих внутренних ресурсах, т.к. вместе с правами на принтеры, папки, и прочую офисную инфраструктуру пользователю сразу будут даны права и на разделы в корпоративном сайте. При этом специалистам IT — отдела не нужно ничего объяснять, они просто делают свою работу “как обычно”.

Для того что бы присвоить пользователям роли, я переопределил класс CPhpAuthManager:

class PhpAuthManager extends CPhpAuthManager {

    public function init() {
        // Иерархию ролей расположим в файле auth.php в директории config приложения
        if ($this->authFile === null) {
            $this->authFile = Yii::getPathOfAlias('application.config.auth') . '.php';
        }

        parent::init();

        // Для гостей у нас и так роль по умолчанию guest.
        if (!Yii::app()->user->isGuest) {
            // Связываем группы из AD с ролями и юзерами
           
            $existingRoles = $this->getRoles();
          
            if (Yii::app()->user->groups) {
                foreach (Yii::app()->user->groups as $group) {
                    if ($existingRoles[$group]) {
                        $this->assign($group, Yii::app()->user->id);
                    }
                }
            }
            
           
        }
    }

}

В коде выше мы берем список групп, в которых состоит пользователь, проверяем: существует ли одноименная роль, и если существует, то присваиваем эту роль пользователю.

Пример конфигурационного файла авторизации с LDAP выглядит так:

	...
	
   /************************************
    ***************ROLES****************
    ************************************/
    'newsReader' => array(
        'type' => CAuthItem::TYPE_ROLE,
        'description' => '',
        'bizRule' => NULL,
        'data' => NULL,
        'children' => array(
            0 => 'readNews',
        ),
    ),
    'newsAuthor' => array(
        'type' => CAuthItem::TYPE_ROLE,
        'description' => '',
        'bizRule' => NULL,
        'data' => NULL,
        'children' => array(
            'newsReader',
            'createNews',
            'updateOwnNews',
            'deleteOwnNews'
        ),
    ),
    
    'newsManager' => array(
        'type' => CAuthItem::TYPE_ROLE,
        'description' => '',
        'bizRule' => NULL,
        'data' => NULL,
        'children' => array(
            'newsReader',
            'createNews',
            'updateNews',
            'deleteNews',
        ),
    ),
    
    //Заявки
    'requestCreator' => array(
        'type' => CAuthItem::TYPE_ROLE,
        'description' => '',
        'bizRule' => NULL,
        'data' => NULL,
        'children' => array(
            0 => 'createRequest',
        ),
    ),
    
    'requestManager' => array(
        'type' => CAuthItem::TYPE_ROLE,
        'description' => '',
        'bizRule' => NULL,
        'data' => NULL,
        'children' => array(
            'createRequest',
            'viewRequests',
            'manageRequests',
        ),
    ),


   /************************************
    **********ROLES ASSIGMENTS**********
    ************************************/
    'developers' => array(
        'type' => CAuthItem::TYPE_ROLE,
        'description' => '',
        'bizRule' => NULL,
        'data' => NULL,
        'children' =>  array(
            'newsManager',
            'requestManager',
        ),
    ),

    'departamentBoss'=> array(
        'type' => CAuthItem::TYPE_ROLE,
        'description' => '',
        'bizRule' => NULL,
        'data' => NULL,
        'children' =>  array(
            'requestCreator' ,
        ),
    ),


В разделе ROLES мы описываем “промежуточные” роли. Затем в разделе ROLES ASSIGMENTS мы описываем группы из AD, и присваиваем к ним промежуточные роли.

Приведенный выше конфиг можно читать так:
Для группы developers будут доступны все действия с новостями (newsManager) и с заявками (requestManager), а для группы departamentBoss будет доступно только создание заявок.

Заключение


Механизм ролей в Yii является по настоящему гибким, если его правильно использовать.
В планах на будущее есть создание или адаптация какого либо GUI решения по управлению ролями, т.к. даже с небольшим количеством действий система становится запутанной, а количество писанины неоправданным.
Призываю всех пользователей к обсуждению: как они реализовывали систему управления правами в Yii проектах, и какие советы из личного опыта могут быть полезны остальным?

Что еще почитать:


Тимофей Яценко @thekip
карма
32,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (26)

  • +4
    Самая крутая статья по RBAC, какую я когда-либо видел
    • +1
      Not bad
      image
  • +1
    Гибкая система, но вот магический bizrul в виде php строки кода, который явно в eval уходит, немного покоробил. Вместо него напрашивается анонимная функция, но я думаю ребята во 2 версии это поправят.
    • 0
      а в чём проблема переопределить метод?

          public function executeBizRule($bizRule, $params, $data)
          {
      
              if ($bizRule instanceof Closure) {
                  return $bizRule($params, $data, $this);
              }
              return parent::executeBizRule($bizRule, $params, $data);
          }
      
      • 0
        Да, это решает проблему.
    • 0
      Мне тоже не понравилось что только строкой, анонимная функция смотрелась бы куда лучше. И так же пришлось шерстить документацию что бы понять можно ли её туда передать или нет.

      Но я подозреваю что разработчики не планировали что люди будут вручную составлять этот файл, предполагается что ты генерируешь его через какой либо ГУИ, а там только строками.
      • 0
        Ну бизнес-правила не всегда такие простые, как в примерах… Там уже гуй не особо поможет, там IDE с автокомплитом и статическим анализом кода нужна, но как выше подсказали, поведение можно переопределить, унаследовавшись от стандартного класса и использовать ананимные функции или даже callable.
    • 0
      Причина, по которой bizrule строка, а не анонимная функция кроется в деталях, отписался ниже
  • 0
    Статья хороша. Мне в своё время пришлось основательно шерстить гугл, чтобы почерпнуть хоть какую нибудь инфу о RBAC.
    Вот тут кавычки забыли
    'actions'=>array(update),
    и тут
    array(allow, 'actions'=>array('create'), 'roles'=>array('createNews'), ),
    А тут «обернуть» в accessRules
    array('allow', 'actions' => array('update'), 'roles' => array( 'updateNews' => array( 'news' => $this->news )), ),
    • 0
      Спасибо, исправил. Просто часть скопировал с IDE с реального проекта, а другую часть дописывал в текстовом редакторе без подсветки, вот и получилась каша.
  • 0
    Господа, существует ли в Yii способ быстро проверять возможность доступа к экшнам контроллера?

    Например, при построении меню с помощью CMenu, возникает необходимость отображать конкретный пункт в зависимости от прав пользователя.
    Или даже в таком случае выгодно просто использовать проверку доступа к операции?
  • +1
    Спасибо за статью. В свое время тоже столкнулся с проблемой написания веб-интерфейса для управления RBAC. Даже с SamDark тут связывался :) Он мне помог постичь суть дзена.
    Но сам так и не дописал более «православную» статью на эту тему.
  • 0
    Стоит добавить, что с точки зрения авторов Yii такой способ использования CAuthManager является не совсем корректным.
    В официальной документации:
    После создания элементов авторизации, компонент authManager (или его наследники, например, CPhpAuthManager, CDbAuthManager) загружает их автоматически. То есть, приведённый код запускается один раз, а НЕ для каждого запроса.
    ________________
    Once we have established this hierarchy, the authManager component (e.g. CPhpAuthManager, CDbAuthManager) will load the authorization items automatically. Therefore, we only need to run the above code one time, and NOT for every request.

    Об этом подробнее рассказано в этой статье www.yiiframework.com/wiki/65
    Это же и является причиной, по которой bizRule является строкой(а не к примеру анонимной функцией) и уходит в eval.( Подробнее см. реализацию — CPhpAuthManager.php->save(); и CPhpAuthManager.php->saveToFile();)

    Но по моему личному мнению — способ, представленный автором в данной статье, как раз — наиболее удобный. В своих проектах реализовал все аналогично.
  • 0
    Вроде тоже самое что в мануале, но намного понятней. Самая большая проблема официальных мануалов по Yii, это ты их читаешь, все вроде ясно, а потом читаешь снова, потому что, в итоге, не фига не ясно.
  • 0
    Действительно очень хорошая статья по RBAC в yii. Давно уже не видел хороших статей по yii. Добавил в закладки.
  • 0
    Не совсем понял как геттер работает, в accessRules скармливаю в параметры $this->news. А когда $this->news то присваивается. По логике вещей, в accessRules должен быть $this->getNews()
    • 0
      $this->news это виртуальное свойство. При обращении к нему вызовется функция $this->getNews().
      Это поведение задается магическим методом __get() класса CComponent, который является прародителем почти для всех классов в Yii.

      www.yiiframework.com/wiki/167/understanding-virtual-attributes-and-get-set-methods/
    • 0
      С Yii не знаком, но рассуждаю логически, могу предположить, что в контроллере магический метод __get() определен таким образом, что при попытке получить несуществующее свойство property, вызывается метод getProperty().
      В данном случае обращение к $this->news приведет к вызову метода $this->getNews()

      опоздал (
  • 0
    \
  • 0
    Вместо геттера я использую переменную для контроллера и пре_фильтр, выполняющий то же самое,
    но только прописанный для действий где нужный объект и так будет использоваться, например update, view, но не create.
    Ваш вариант здесь получается лучше.

    А для того, чтобы обрабатывать сложные бизнес правила я обычно в проекте создаю класс (Access) со статическими методами и константами с названиями действий и ролей, которые и прописываю в checkAccess.
    Константы позволяют избежать ошибок при при вызове правила в разных местах;
    А использование отдельных функций вместо написания их непосредственно в bizRule убивает двух зайцев: код обрабатывается IDE и не нужно в случае изменения логики править записи в базе (для DbAuth)
      // в контроллере:
     public function accessRules() {
        return array(
          array('allow',
            'actions' => array('create', 'delete', 'update'),
            'roles' => array(Access::EDIT_ARTICLE => array('article' => $this->article)),
          ),
       )
    


    class Access
    {
      /** Доступные роли
      ================================ */
      const ROLE_USER = 'user';
    
      /** Доступные действия для ролей
      ================================ */
      const EDIT_ARTICLE = 'editArticle';
    
      /** Правила
      ================================ */
      static public $bizRules = array(
        self::EDIT_ARTICLE => 'return Access::editArticle($params["article"]);',
      )
    
      /** Само сложное правило
      ================================ */
      static public function editArticle($article) {
        return  random(1);  // наша логика
      }
    }
  • 0
    статья супер, подскажите новичку, а где эти роли и правила прописывать??? никак не могу понять.
    • 0
      // Иерархию ролей расположим в файле auth.php в директории config приложения
              if ($this->authFile === null) {
                  $this->authFile = Yii::getPathOfAlias('application.config.auth') . '.php';
              }
      
      • 0
        спасибо!
    • 0
      Так же можете в базе данных в соответствующих таблицах (Auth*).

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.