Случилось мне давече писать динамическую систему ролей и прав для интернет-магазина.
Поскольку в интернете ничего нужного, кроме вот этой серии статей не нашел, придумал написать все сам.
Итак, приступим.
Хранить информацию о ролях, ресурсах и правах доступа мы будем в базе. База имеет примерно следующую структуру:
acl_resources — ресурсы
acl_roles — группы юзернеймов, они же — роли
acl_cross — перекресток соответствий ролей и ресурсов:
id / role_id / res_id
1 1 1
2 1 2
3 1 3
* Для роли с id=1 доступны ресурсы 1,2,3
В файле с контролем доступа напишем вот такой код:
class Acl extends Zend_Acl
{
public function __construct()
{
$model = new User();
/**
* добавляем ресурсы
*/
// получаем ресурсы
$resources = $model->getResources();
foreach($resources as $mod)
{
// если такой ресурс еще не добавлен
if(false == $this->has($mod['module'].'_'.$mod['controller']))
{
// добавляем
$this->add(new Zend_Acl_Resource($mod['module'].'_'.$mod['controller']));
}
}
/**
* добавляем роли пользователей
* и сразу проставляем им разрешения
*/
// достаем роли
$roles = $model->getRoles();
// для каждой роли
foreach($roles as $role)
{
// производим добавление
$this->addRole(new Zend_Acl_Role($role['name']));
// достаем доступные для этой роли ресурсы
$permissions = $model->getRoleResources($role['id']);
// проставляем разрешения на эти ресурсы
foreach($permissions as $perm)
{
$this->allow( $role['name'],
$perm['module'].'_'.$perm['controller'],
$perm['action']);
}
}
}
}
Таким образом мы обрабатываем записи из базы и создаем, собственно, ACL.
Дальше мы передаем управление плагину. Плагин будет обрабатывать права доступа на уровне контроллера. Ну, это, напримиер, если обычный пользователь скопировал модераторскую ссылку на действие удаления товара — чтоб его не пустило. Вот примерный код:
class Lord_Plugin_Acl extends Zend_Controller_Plugin_Abstract
{
private $_acl = null;
public function __construct(Zend_Acl $acl)
{
$this->_acl = $acl;
}
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
// Получаем объект Zend_Layout
$layout = Zend_Layout::getMvcInstance();
// Получаем объект вида
$view = $layout->getView();
$auth = Zend_Auth::getInstance();
// если есть какой-то пользователь в хранилище сессии
if ($auth->hasIdentity() == true)
{
// получаем данные этого пользователя
$user = $auth->getIdentity();
$userModel = new User();
$role = $userModel->getRole($user->role);
$user->roleName = $role->name;
}
else
{
$user->roleName = 'anon';
}
// кладем в переменную вида, для отображения
$view->user = $user;
/*--------------------------------------------------------------*/
$request = $this->getRequest();
$controller = $request->getControllerName();
$module = $request->getModuleName();
$action = $request->getActionName();
$acl = new Acl();
$isAllow = $acl->isAllowed($user->roleName, $module.'_'.$controller, $action);
if($isAllow == false)
{
$request->setModuleName('user')
->setControllerName('login')
->setActionName('index');
}
$view->acl = $acl;
/*--------------------------------------------------------------*/
}
}
Теперь нам нужно скрывать и показывать элементы управления на уровне вида приложения. Для этого мы напишем хэлпер, который проверял бы права доступа и показывал или не показывал элемент управления:
class Lord_View_Helper_Acl
{
public function Acl($module = 'default',
$controller = 'index',
$action = 'index')
{
// Получаем объект Zend_Layout
$layout = Zend_Layout::getMvcInstance();
// Получаем объект вида
$view = $layout->getView();
$acl = new Acl();
$isAllow = $acl->isAllowed($view->user->roleName, $module.'_'.$controller, $action);
if($isAllow == false)
{
return false;
}
else
return true;
}
}
После написания хэлпера в скрипте вида нам достаточно написать примерно такой вот код, чтоб скрыть или показать элемент управления:
<?if($this->acl('store', 'categories', 'add')==true):?>
<img src="<?=$this->imagesUrl?>icons/icon_add.png"
title = "<?=$this->translate('store_category_primary_add');?>">
<?endif;?>
В результате имеем систему, которая из вэбинтерфейса может раздавать права любой группе на любой модуль/контроллер/действие.
Немного кода для скачивания:
— База
— Плагин
— ViewHelper
— ACL
Поскольку в интернете ничего нужного, кроме вот этой серии статей не нашел, придумал написать все сам.
Итак, приступим.
База
Хранить информацию о ролях, ресурсах и правах доступа мы будем в базе. База имеет примерно следующую структуру:
acl_resources — ресурсы
acl_roles — группы юзернеймов, они же — роли
acl_cross — перекресток соответствий ролей и ресурсов:
id / role_id / res_id
1 1 1
2 1 2
3 1 3
* Для роли с id=1 доступны ресурсы 1,2,3
ACL
В файле с контролем доступа напишем вот такой код:
class Acl extends Zend_Acl
{
public function __construct()
{
$model = new User();
/**
* добавляем ресурсы
*/
// получаем ресурсы
$resources = $model->getResources();
foreach($resources as $mod)
{
// если такой ресурс еще не добавлен
if(false == $this->has($mod['module'].'_'.$mod['controller']))
{
// добавляем
$this->add(new Zend_Acl_Resource($mod['module'].'_'.$mod['controller']));
}
}
/**
* добавляем роли пользователей
* и сразу проставляем им разрешения
*/
// достаем роли
$roles = $model->getRoles();
// для каждой роли
foreach($roles as $role)
{
// производим добавление
$this->addRole(new Zend_Acl_Role($role['name']));
// достаем доступные для этой роли ресурсы
$permissions = $model->getRoleResources($role['id']);
// проставляем разрешения на эти ресурсы
foreach($permissions as $perm)
{
$this->allow( $role['name'],
$perm['module'].'_'.$perm['controller'],
$perm['action']);
}
}
}
}
Таким образом мы обрабатываем записи из базы и создаем, собственно, ACL.
Плагин
Дальше мы передаем управление плагину. Плагин будет обрабатывать права доступа на уровне контроллера. Ну, это, напримиер, если обычный пользователь скопировал модераторскую ссылку на действие удаления товара — чтоб его не пустило. Вот примерный код:
class Lord_Plugin_Acl extends Zend_Controller_Plugin_Abstract
{
private $_acl = null;
public function __construct(Zend_Acl $acl)
{
$this->_acl = $acl;
}
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
// Получаем объект Zend_Layout
$layout = Zend_Layout::getMvcInstance();
// Получаем объект вида
$view = $layout->getView();
$auth = Zend_Auth::getInstance();
// если есть какой-то пользователь в хранилище сессии
if ($auth->hasIdentity() == true)
{
// получаем данные этого пользователя
$user = $auth->getIdentity();
$userModel = new User();
$role = $userModel->getRole($user->role);
$user->roleName = $role->name;
}
else
{
$user->roleName = 'anon';
}
// кладем в переменную вида, для отображения
$view->user = $user;
/*--------------------------------------------------------------*/
$request = $this->getRequest();
$controller = $request->getControllerName();
$module = $request->getModuleName();
$action = $request->getActionName();
$acl = new Acl();
$isAllow = $acl->isAllowed($user->roleName, $module.'_'.$controller, $action);
if($isAllow == false)
{
$request->setModuleName('user')
->setControllerName('login')
->setActionName('index');
}
$view->acl = $acl;
/*--------------------------------------------------------------*/
}
}
ViewHelper
Теперь нам нужно скрывать и показывать элементы управления на уровне вида приложения. Для этого мы напишем хэлпер, который проверял бы права доступа и показывал или не показывал элемент управления:
class Lord_View_Helper_Acl
{
public function Acl($module = 'default',
$controller = 'index',
$action = 'index')
{
// Получаем объект Zend_Layout
$layout = Zend_Layout::getMvcInstance();
// Получаем объект вида
$view = $layout->getView();
$acl = new Acl();
$isAllow = $acl->isAllowed($view->user->roleName, $module.'_'.$controller, $action);
if($isAllow == false)
{
return false;
}
else
return true;
}
}
После написания хэлпера в скрипте вида нам достаточно написать примерно такой вот код, чтоб скрыть или показать элемент управления:
<?if($this->acl('store', 'categories', 'add')==true):?>
<img src="<?=$this->imagesUrl?>icons/icon_add.png"
title = "<?=$this->translate('store_category_primary_add');?>">
<?endif;?>
В результате имеем систему, которая из вэбинтерфейса может раздавать права любой группе на любой модуль/контроллер/действие.
Немного кода для скачивания:
— База
— Плагин
— ViewHelper
— ACL