RBAC - управление правами на основе ролей

В новой версии появился новый модуль Rbac — модуль управления правами на основе ролей и разрешений (RBAC).
Суть его сводится к тому, что теперь в админке можно создавать различные роли, давать ролям необходимые права (разрешения) и назначать эти роли пользователям. Роли могут наследовать от других ролей. Таким образом можно очень гибко настраивать систему управления правами.








Для работы с модулем в основном используются три метода: IsAllow для проверки прав текущего пользователя, GetMsgLast для получения последнего сообщения об ошибке, ReturnActionError удобный враппер для перенаправления на страницу ошибки с нужным сообщением.
// для текущего пользователя
$this->Rbac_IsAllow('topic_create');
// для конкретного пользователя с параметрами
$this->Rbac_IsAllowUser($oUser,'topic_update',array('topic'=>$oTopic));
// для плагина 'article', указывается код плагина
$this->Rbac_IsAllow('article_create','article');
 // для плагина, где $this - любой текущий объект плагина (кроме Inherit классов)
$this->Rbac_IsAllow('article_create',$this);
// для плагина с параметрами
 $this->Rbac_IsAllow('article_update',$this,array('article'=>$oArticle));


Помимо простой проверки на наличие разрешений, можно добавить кастомную проверку прав. Для этого необходимо наследованием в модуль Rbac добавить свой метод. Пример для плагина Article:
class PluginArticle_ModuleRbac extends PluginArticle_Inherit_ModuleRbac {
	/**
	 * Проверка прав на редактирование статьи
	 *
	 * @param $oUser
	 * @param $aParams
	 *
	 * @return bool
	 */
	public function CheckCustomPluginArticleUpdate($oUser,$aParams) {
		if (!$oUser or !isset($aParams['article'])) {
			return false;
		}
		/**
		 * Допускаем до редактирования всех с разрешением 'update_all'
		 */
		if ($this->Rbac_IsAllowUser($oUser,'update_all','article')) {
			return true;
		}
		/**
		 * Допускаем до редактирования автора статьи
		 */
		if ($aParams['article']->getUserId()==$oUser->getId()) {
			return true;
		}
		/**
		 * Запрещаем
		 */
		return false;
	}
}

Как видно из примера, название метода формируется из 'CheckCustom'+PluginName(если разрешение принадлежит плагину)+PermissionCodeName(код самого разрешения).

Сама проверка в коде (экшен управления статьями) выглядит так:
/**
 * Права на редактирование
 */
if (!$this->Rbac_IsAllow('update',$this,array('article'=>$oArticle))) {
	return $this->Rbac_ReturnActionError(true);
}

/**
 * Права на просмотр
 */
if (!$this->Rbac_IsAllow('view',$this)) {
	return $this->Rbac_ReturnActionError();
}

Параметр true у метода ReturnActionError сообщает о том, что данный метод вызывается из стандартной админики, это необходимо для корректного отображения страницы с ошибкой.

Плагины обычно имеют предустановленный набор прав, который нужно создать при активации плагина. Для этого есть специальный метод CreatePermissions, в который передается набор данных с необходимыми правами. Вот пример для плагина Article:
public function Activate() {
                ........

		/**
		 * Создаем необходимые права доступа
		 */
		$aData=array(
			'groups' => array(
				array('article','Статьи'),
			),
			'roles' => array(
				array('article_moderator','Модератор статей'),
			),
			'permissions' => array(
				array('view','Просмотр статьи','msg_error'=>'У вас нет прав на просмотр статьи','group'=>'article','roles'=>array('guest','user')),
				array('create','Создание статей','msg_error'=>'У вас нет прав на создание статьи','group'=>'article','roles'=>'user'),
				array('update_all','Правка всех статей','msg_error'=>'У вас нет прав на редактирование статьи','group'=>'article','roles'=>'article_moderator'),
				array('update','Правка своих статей','msg_error'=>'У вас нет прав на редактирование статьи','group'=>'article','roles'=>'user'),
			),
		);
		if (!$this->Rbac_CreatePermissions($aData,$this)) {
			return false;
		}

                ......
}


При деактивации плагина делаем так:
$this->Rbac_RemovePermissions(array(
				'groups' => 'article',
				'roles' => 'article_moderator',
				),$this);


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

21 комментарий

avatar
Круто! Долго ждали такой функционал!
avatar
Раз уж так пошло, почему бы просто не переписать livestreet под yii? Зачем изобретать столько велосипедов?
  • bsvi
  • -1
avatar
Затем, что ЛС становится нормальным фреймворком, нет?
avatar
Нет, фреймворк ЛС сейчас становится… yii, только написанный ort'ом. Если посмотрите последние «нововведения», то они взяты из yii, но зачем было их «изобретать», если они и так есть?
avatar
Честно говоря, звучит странно… Зачем было изобретать, скажем, C# или PHP если до фига того, что в них есть было и в других языках? Тем не менее изобрели и успешно ими пользуются.

Что касается, в частности, модели RBAC это вообще, мягко говоря, никаким боком не относится к yii. Немного непонятно, что вас смущает-то? Похожесть синтаксиса или что?
avatar
потому что структура лс — не уии. зачем другие фреймворки появляются если есть «более старый»? ответ очевиден — значит есть какие-то преимущества, новый подход.
avatar
а как насчет работы с пользователями, хотелось бы иметь возможность просмотра айпи, изменения, удаления комментариев и возможность банить с выбором времени и при этом все не где-то там, а здесь и сейчас так как это реализовано на форумах и ВК
avatar
а как насчет работы с пользователями, хотелось бы иметь возможность просмотра айпи, изменения, удаления комментариев и возможность банить с выбором времени
уже давно реализовано в админке.
avatar
А как можно будет делать, чтобы, например, создание топиков былло разрешено только пользователем с рейтингом выше 5?
avatar
Это уже сейчас есть, в 1.0.3
avatar
Смысл вопроса совершенно в другом.
комментарий был удален
avatar
Ребят. Как в шаблоне сделать проверку на группу пользователя?
avatar
Поконкретнее пожалуйста
avatar
Хочу в шаблоне показывать стат. инфу доступную только для конкретной роли пользователей созданной в админке. Интересует конструкция типа:
{if $oUserCurrent && группа_пользователя='индентификатор_группы'}
    контент
{/if}
avatar
Можно через блок
{insert name='block' block='statInfo' params=[ 'user' => $oUserCurrent, 'role' => 'код_роли']}

BlockStatInfo.class.php:
class BlockStatInfo extends Block
{
    public function Exec()
    {
        $sRole = $this->GetParam('role');
        $oUser = $this->GetParam('user')
        if(!$oUser){
            return false;
        }
        $aRoles = $this->Rbac_GetRolesByUser( $oUserCurrent);
        $aRoleCodes = [];
        foreach($aRoles as $aRole){
            $aRoleCodes[] = $aRole->getCode();
        }
        if(!in_array($sRole, $aRoleCodes)){
            return false;
        }
        $this->SetTemplate('путь к шаблону');
    }
}
avatar
Не работает, вставил блок после юзербара. В итоге загрузился только юзербар, а дальше пустая страница
avatar
Код не полный. Это вариант решения. Нужно вставить путь к шаблону блока, например создать компонент шаблона. Вот рабочий вариант, без ошибок.
avatar
role: Это код роли в админке
{insert name='block' block='statInfo' params=[ 'user' => $oUserCurrent, 'role' => 'user']}

classes/blocks/BlockStatInfo.class.php:
class BlockStatInfo extends Block
{
    public function Exec()
    {
        $sRole = $this->GetParam('role');
        $oUser = $this->GetParam('user');
        if(!$oUser){
            return false;
        }
        $aRoles = $this->Rbac_GetRolesByUser( $oUser);
        $aRoleCodes = [];
        foreach($aRoles as $aRole){
            $aRoleCodes[] = $aRole->getCode();
        }
        if(!in_array($sRole, $aRoleCodes)){
            return false;
        }
    }
}


frontend/skin/ваш_шаблон/blocks/block.statInfo.tpl:
Здесь html блока
avatar
Спасибо, работает.

Но если вернуться к первому варианту, что я делаю не так? Создал BlockStatInfo.class.php в application/classes/blocks с содержимым
<?php

class BlockStatInfo extends Block
{
    public function Exec()
    {
        $sRole = $this->GetParam('role');
        $oUser = $this->GetParam('user')
        if(!$oUser){
            return false;
        }
        $aRoles = $this->Rbac_GetRolesByUser( $oUserCurrent);
        $aRoleCodes = [];
        foreach($aRoles as $aRole){
            $aRoleCodes[] = $aRole->getCode();
        }
        if(!in_array($sRole, $aRoleCodes)){
            return false;
        }
        $this->SetTemplate('component@test.test');
    }
}

Создал в application/frontend/components компонент test с шаблоном test
{
    "name": "test",
    "version": "1.0.0",
    "dependencies": {
        "nav": "*"
    },
    "templates": {
        "test": "test.tpl"
    },
    "styles": {
        "test": "css/test.css"
    }
}

В конфиге шаблона подключил. В шаблоне прописал вставил
{insert name='block' block='statInfo' params=[ 'user' => $oUserCurrent, 'role' => 'user']}

Все что идет до блока загружается, все что после, включая сам блок — нет.
avatar
Похоже разобрался. Если в BlockStatInfo.class.php $oUserCurrent заменить на $oUser, то магия начинает работать
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.