Практически в каждом проекте возникает необходимость реализовать регистрацию и авторизацию пользователей. В рецепте RBAC и описание ролей в файле подробно описана реализация распределения доступа пользователям по ролям. Этот рецепт является уточнением рецепта Аутентификация и авторизация, в котором и описано использование доступа по ролям. Попробуем сделать настройку ролей пользователей более гибкой.

В примере из рецепта обозначены четыре роли:

return array(
    'guest' => array(
        'type' => CAuthItem::TYPE_ROLE,
        'description' => 'Гость',
        'bizRule' => null,
        'data' => null
    ),
 
    'user' => array(
        'type' => CAuthItem::TYPE_ROLE,
        'description' => 'Пользователь',
        'children' => array(
            'guest',
        ),
        'bizRule' => null,
        'data' => null
    ),
 
    'moderator' => array(
        'type' => CAuthItem::TYPE_ROLE,
        'description' => 'Модератор',
        'children' => array(
            'user',
        ),
        'bizRule' => null,
        'data' => null
    ),
 
    'administrator' => array(
        'type' => CAuthItem::TYPE_ROLE,
        'description' => 'Администратор',
        'children' => array(
            'moderator', // позволим админу всё, что позволено модератору
        ),
        'bizRule' => null,
        'data' => null
    ),
);

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

class PostAdminController extends СController
{
    public function filters()
    {
        return array(
            'accessControl',
        );
    }
 
    public function accessRules()
    {
        return array(
            array('allow',
                'roles'=>array('administrator'),
            ),
            array('deny',
                'users'=>array('*'),
            ),
        );
    }
}

и доступ к удалению комментариев только модераторам:

class CommentAdminController extends СController
{
    public function filters()
    {
        return array(
            'accessControl',
        );
    }
 
    public function accessRules()
    {
        return array(
            array('allow',
                'roles'=>array('moderator'),
            ),
            array('deny',
                'users'=>array('*'),
            ),
        );
    }
}

Заметим, что администратор наследует права модератора, поэтому администратор тоже получит доступ к комментариям.

Здесь мы использовали простую иерархию и указывали разрешения на основе ролей:

rbac-simple

В любом из проектов однажды может появиться несколько одноуровневых ролей (администратор, модератор, контент-менеджер, представитель компании и т.д.) и потребуется независимо разделять доступ для каждой роли к администрированию разных разделов сайта. Причём должна быть возможность гибкого управления разрешениями. Настраивать их наследование друг от друга не очень удобно, так как каждый должен иметь порой несовместимые разрешения.

Для реализации такого разделения вместо разрешения по ролям удобно воспользоваться системой разрешений на основе операций.

Предположим, что у нас должны быть пять ролей:

  • Гость
  • Пользователь
  • Принятый пользователь
  • Модератор
  • Контент-менеджер
  • Администратор

Пусть модератору нужно доверить работу с комментариями и пользователями, а контент-менеджеру – с блогом и статическими страницами. Также обычный пользователь не должен входить в корпоративный форум и в админку.

Для реализации такого распределения стоит отказаться от разделения по именам ролей в пользу разделения по именам конкретных операций.

Введём несколько атомарных разрешений для каждого раздела сайта, например:

  • profile (доступ к личному кабинету)
  • forum (доступ к корпоративному форуму)
  • control_panel (доступ к панели управления)
  • m_pages (доступ к администрированию страниц)
  • m_blogs (доступ к администрированию блога)
  • m_comments (доступ к администрированию комментариев)
  • m_users (доступ к администрированию пользователей)

Теперь настроим иерархию ролей и для каждой роли укажем список разрешённых действий. Иерархия может быть, например, такой:

rbac-full

Теперь в фильтрах доступа наших контроллеров помимо имён ролей можно использовать имена операций.

class PostAdminController extends СController
{
    public function filters()
    {
        return array(
            'accessControl',
        );
    }
 
    public function accessRules()
    {
        return array(
            array('allow',
                'roles'=>array('m_blogs'),
            ),
            array('deny',
                'users'=>array('*'),
            ),
        );
    }
}
class CommentAdminController extends СController
{
    public function filters()
    {
        return array(
            'accessControl',
        );
    }
 
    public function accessRules()
    {
        return array(
            array('allow',
                'roles'=>array('m_comments'),
            ),
            array('deny',
                'users'=>array('*'),
            ),
        );
    }
}

При таком подходе контроллеры имеют зависимость от разрешений m_blogs и m_comments вместо зависимости от ролей, что даёт возможность легко вводить новые роли и добавлять каждой роли свои разрешения не переписывая правила контроллеров.

В файле auth.php для ролей и разрешений нужно использовать соответственно записи типовCAuthItem::TYPE_ROLE и CAuthItem::TYPE_OPERATION и прописать все наследования:

return array(
 
    /* Разрешения */
 
    'profile' => array(
        'type' => CAuthItem::TYPE_OPERATION,
        'description' => 'Доступ в личный кабинет',
        'bizRule' => null,
        'data' => null
    ),
 
    'forum' => array(
        'type' => CAuthItem::TYPE_OPERATION,
        'description' => 'Доступ в форум',
        'bizRule' => null,
        'data' => null
    ),
 
    'control_panel' => array(
        'type' => CAuthItem::TYPE_OPERATION,
        'description' => 'Доступ в панель управления',
        'bizRule' => null,
        'data' => null
    ),
 
    'm_comments' => array(
        'type' => CAuthItem::TYPE_OPERATION,
        'description' => 'Управление комментариями',
        'bizRule' => null,
        'data' => null
    ),
 
    'm_pages' => array(
        'type' => CAuthItem::TYPE_OPERATION,
        'description' => 'Управление страницами',
        'bizRule' => null,
        'data' => null
    ),
 
    'm_users' => array(
        'type' => CAuthItem::TYPE_OPERATION,
        'description' => 'Управление пользователями',
        'bizRule' => null,
        'data' => null
    ),
 
    'm_blogs' => array(
        'type' => CAuthItem::TYPE_OPERATION,
        'description' => 'Управление блогом',
        'bizRule' => null,
        'data' => null
    ),
 
    /* Роли */    
 
    'role_guest' => array(
        'type' => CAuthItem::TYPE_ROLE,
        'description' => 'Гость',
        'bizRule' => null,
        'data' => null
    ),
 
    'role_user' => array(
        'type' => CAuthItem::TYPE_ROLE,
        'description' => 'Пользователь',
        'children' => array(
            'role_guest',
            'profile'
        ),
        'bizRule' => null,
        'data' => null
    ),
 
    'role_active_user' => array(
        'type' => CAuthItem::TYPE_ROLE,
        'description' => 'Принятый пользователь',
        'children' => array(
            'role_user',
            'forum'
        ),
        'bizRule' => null,
        'data' => null
    ),
 
    'role_moderator' => array(
        'type' => CAuthItem::TYPE_ROLE,
        'description' => 'Модератор',
        'children' => array(
            'role_active_user',
            'control_panel',
            'm_comments',
            'm_users',
        ),
        'bizRule' => null,
        'data' => null
    ),
 
    'role_manager' => array(
        'type' => CAuthItem::TYPE_ROLE,
        'description' => 'Контент-менеджер',
        'children' => array(
            'role_active_user',
            'control_panel',
            'm_blogs',
            'm_pages',
        ),
        'bizRule' => null,
        'data' => null
    ),
 
    'role_admin' => array(
        'type' => CAuthItem::TYPE_ROLE,
        'description' => 'Администратор',
        'children' => array(
            'role_active_user',
            'control_panel',
            'm_pages',
            'm_blogs',
            'm_comments',
            'm_users',
        ),
        'bizRule' => null,
        'data' => null
    ),
);

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

blog_read_post
blog_add_post
blog_edit_post
blog_edit_own_post
blog_delete_post

Потом ввести роли c наследованием данных задач:

Читатель
    blog_read_post
Редактор
    blog_add_post
    blog_edit_own_post
Модератор
    blog_add_post
    blog_edit_post
    blog_delete_post

Этот способ рассмотрен в другой подробной статье.

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

Этот принцип разделения на роли и разрешения подойдёт для любого способа хранения (в PHP файле, в базе данных и т.д.), и многие существующие расширения для Yii включают его использование.

Взято отсюда

Комментарии

comments powered by Disqus