На DevZone появилась вторая часть статьи о интреграции Zend_Acl в MVC Альдемара Бернала и я спешу предложить вам ее перевод.
В первой части мы говорили о том, как настроить экземпляр Zend_Acl и включить его в окружение MVC (с использованием плагина фронт-контроллера). Но как же настройки других действий для блокирования доступа, или как сделать редактирование статьи только её автором? Это и кое-что еще будет рассмотрено далее.
Как я уже говорил в первой части, эта статья основана на следующем предложении, которое в настоящий момент находится в стадии исследования.
Давайте поговорим о модулях. В качестве примера для данной статьи мы взяли сайт подобный ДевЗоне. А что если мы создадим административный модуль, задачей которого будет реализация процедуры одобрения статей. Кроме того в модуле можно реализовать еще ряд задач, таких как управление короткими ссылками или категориями. В этом случае нам придется изменить нашу модель ресурсов:
Общий модуль
Административный модуль
Основываясь на этой новой модели ресурсов, мы создадим экземпляр Zend_Acl, который будет отражает её.
Примечание: помните, что этот код и создание экземпляра объекта Zend_Acl должно исполняться перед вызовом метода диспетчеризации фронт-контроллера (во время загрузки).
Отмечу, что как и раньше, мы создали ресурс для каждого контроллера. Но в случае с модулем администратора, мы создали ресурс для модуля и по одному для каждого контроллера внутри этого модуля в формате «модуль: контроллер», сделав их потомками ресурса модуля. Так же мы сделали роль Администратор единственной, кому разрешен доступ ко всему модулю администрирования.
Как только пользователь вошел в приложение, он должен получить роль. В нашем примере эта роль может быть «гость», «автор» или «администратор». Но как мы можем изменить текущую ACL-роль в нашем компоненте? Во-первых, вы должны сохранить эту роль в переменной, находящейся в пространстве сессии. Таким образом, как только пользователь входит, вы должны сохранить роль пользователя в сессии. При следующем запросе, вы возьмете эту переменную из сессии и используете её для настройки плагина фронт-контроллера во время фазы загрузки.
Контроллер пользователей
Файл-загрузчик
Может быть, некоторым из вас (надеюсь, никому =D) просто не нравится идея иметь действие «Доступ запрещен» в контроллере ошибок, или просто хочется назвать его каким-либо иначе. Это можно сделать, вызвав метод setErrorPage плагина фронт-контроллера.
Метод setErrorPage может вызываться с указанием только имени действия. В этом случае контроллер и модуль останутся «error» и «default». Так же метод может вызываться с передачей названий действия и контроллера, либо передав все три параметра.
Наконец, мы увидим один из наиболее важных частей этого предложения. До сих пор в нашем примере ДевЗоны мы видели, что мы разрешаем администраторам и авторам редактировать статьи. Но постойте, есть еще недостающая часть нашего приложения. Сейчас если я автор и имею доступ к article/edit/:id, это значит, что я имею доступ к редактированию не только своих статей, но и статей остальных авторов! Это не очень хорошо, так ведь? Итак, что мы будем с этим делать? Мы будем управлять этим используя помощник действий, а это значит, что вы сможете получить доступ к нашим ACL внутри любого контроллера, а не только во время загрузки.
Итак, первое, что мы делаем, это регистрация не только нашего плагина фронт-контролера, но и нашего помощника действий в Брокере помощников действий контроллера.
Файл-загрузчик
И после регистрации помощника, мы можем использовать его внутри любого контроллера. Давайте дадим права доступа на изменение только для владельца или любого администратора.
Контроллер статей
Некоторые любители копошиться в мусоре всю свою жизнь пытаются найти недостающее звено (и они так и умрут в поиске его) и некоторые ЗФ-еры состарятся в попытках заставить работать ACL должным образом в их MVC-окружении. Надеюсь, высказанное выше предложение, может оказаться одним из недостающих кусочков в мире ACL + MVC.
В заключении хочу дать рекомендацию. Придерживайтесь принципа «Делай проще»: если вам не нужна позарез динамическая загрузка ACL, то его загрузка и настройка вручную — совсем не грех, возможно это лучший способ действий в данной ситуации.
Для более подробного ознакомления с темой вы можете почитать следующее:
Zend_Acl & MVC Integration
и небольшой пример реализации подхода, описанного в статье:
Source Code
Crosspost: http://lobach.info/develop/zf/zend_acl-and-mvc-integration-part-2/
В первой части мы говорили о том, как настроить экземпляр Zend_Acl и включить его в окружение MVC (с использованием плагина фронт-контроллера). Но как же настройки других действий для блокирования доступа, или как сделать редактирование статьи только её автором? Это и кое-что еще будет рассмотрено далее.
Как я уже говорил в первой части, эта статья основана на следующем предложении, которое в настоящий момент находится в стадии исследования.
1. Использование модулей
Давайте поговорим о модулях. В качестве примера для данной статьи мы взяли сайт подобный ДевЗоне. А что если мы создадим административный модуль, задачей которого будет реализация процедуры одобрения статей. Кроме того в модуле можно реализовать еще ряд задач, таких как управление короткими ссылками или категориями. В этом случае нам придется изменить нашу модель ресурсов:
Общий модуль
- Контроллер пользователей.
- Контроллер статей.
Административный модуль
- Контроллер статей.
- Контроллер коротких ссылок.
- Контроллер категорий.
Основываясь на этой новой модели ресурсов, мы создадим экземпляр Zend_Acl, который будет отражает её.
Примечание: помните, что этот код и создание экземпляра объекта Zend_Acl должно исполняться перед вызовом метода диспетчеризации фронт-контроллера (во время загрузки).
/** Creating Roles */ require_once 'Zend/Acl/Role.php'; $myAcl->addRole(new Zend_Acl_Role('guest')) ->addRole(new Zend_Acl_Role('writer'), 'guest') ->addRole(new Zend_Acl_Role('admin'), 'writer'); /** Creating resources */ require_once 'Zend/Acl/Resource.php'; /** Default module */ $myAcl->add(new Zend_Acl_Resource('user')) ->add(new Zend_Acl_Resource('article')); /** Admin module */ $myAcl->add(new Zend_Acl_Resource('admin')) ->add(new Zend_Acl_Resource('admin:article', 'admin')) ->add(new Zend_Acl_Resource('admin:quick-link', 'admin')) ->add(new Zend_Acl_Resource('admin:category', 'admin')); /** Creating permissions */ $myAcl->allow('guest', 'user') ->deny('guest', 'article') ->allow('guest', 'article', 'view') ->allow(array('writer', 'admin'), 'article', array('add', 'edit')) ->allow('admin', 'admin'); /** Setting up the front controller */ require_once 'Zend/Controller/Front.php'; $front = Zend_Controller_Front::getInstance(); $front->setControllerDirectory(array('default' => 'path/to/default/controllers', 'admin' => 'path/to/admin/controllers')); /** Registering the Plugin object */ require_once 'Zend/Controller/Plugin/Acl.php'; $front->registerPlugin(new Zend_Controller_Plugin_Acl($myAcl, 'guest')); /** Dispatching the front controller */ $front->dispatch();
Отмечу, что как и раньше, мы создали ресурс для каждого контроллера. Но в случае с модулем администратора, мы создали ресурс для модуля и по одному для каждого контроллера внутри этого модуля в формате «модуль: контроллер», сделав их потомками ресурса модуля. Так же мы сделали роль Администратор единственной, кому разрешен доступ ко всему модулю администрирования.
2. Использование ролей
Как только пользователь вошел в приложение, он должен получить роль. В нашем примере эта роль может быть «гость», «автор» или «администратор». Но как мы можем изменить текущую ACL-роль в нашем компоненте? Во-первых, вы должны сохранить эту роль в переменной, находящейся в пространстве сессии. Таким образом, как только пользователь входит, вы должны сохранить роль пользователя в сессии. При следующем запросе, вы возьмете эту переменную из сессии и используете её для настройки плагина фронт-контроллера во время фазы загрузки.
Контроллер пользователей
class UserController extends Zend_Controller_Action { protected $_application; public function init() { require_once 'Zend/Session/Namespace.php'; $this->_application = new Zend_Session_Namespace('myApplication'); } public function loginAction() { ... Validation code if ($valid) { /** Setting role into session */ $this->_application->currentRole = $user->role; $this->_application->loggedUser = $user->username; } } public function logoutAction() { $this->_application->currentRole = 'guest'; $this->_application->loggedUser = null; } }
Файл-загрузчик
/** Loading application from session */ require_once 'Zend/Session/Namespace.php'; $application = new Zend_Session_Namespace('myApplication'); if (!isset($application->currentRole)) { $application->currentRole = 'guest'; } /** Setting up the front controller */ require_once 'Zend/Controller/Front.php'; $front = Zend_Controller_Front::getInstance(); $front->setControllerDirectory('path/to/controllers'); /** Registering the Plugin object */ require_once 'Zend/Controller/Plugin/Acl.php'; $front->registerPlugin(new Zend_Controller_Plugin_Acl($myAcl, $application->currentRole)); /** Dispatching the front controller */ $front->dispatch();
3. Установка действия при ошибке «запрет доступа»
Может быть, некоторым из вас (надеюсь, никому =D) просто не нравится идея иметь действие «Доступ запрещен» в контроллере ошибок, или просто хочется назвать его каким-либо иначе. Это можно сделать, вызвав метод setErrorPage плагина фронт-контроллера.
/** Setting up the front controller */ require_once 'Zend/Controller/Front.php'; $front = Zend_Controller_Front::getInstance(); $front->setControllerDirectory('path/to/controllers'); /** Setting default access denied action */ require_once 'Zend/Controller/Plugin/Acl.php'; $aclPlugin = new Zend_Controller_Plugin_Acl($myAcl, 'guest'); $aclPlugin->setErrorPage('goaway', 'my-error-controller', 'my-module'); /** Registering the Plugin object */ $front->registerPlugin($aclPlugin); /** Dispatching the front controller */ $front->dispatch();
Метод setErrorPage может вызываться с указанием только имени действия. В этом случае контроллер и модуль останутся «error» и «default». Так же метод может вызываться с передачей названий действия и контроллера, либо передав все три параметра.
4. Использование помошника действий
Наконец, мы увидим один из наиболее важных частей этого предложения. До сих пор в нашем примере ДевЗоны мы видели, что мы разрешаем администраторам и авторам редактировать статьи. Но постойте, есть еще недостающая часть нашего приложения. Сейчас если я автор и имею доступ к article/edit/:id, это значит, что я имею доступ к редактированию не только своих статей, но и статей остальных авторов! Это не очень хорошо, так ведь? Итак, что мы будем с этим делать? Мы будем управлять этим используя помощник действий, а это значит, что вы сможете получить доступ к нашим ACL внутри любого контроллера, а не только во время загрузки.
Итак, первое, что мы делаем, это регистрация не только нашего плагина фронт-контролера, но и нашего помощника действий в Брокере помощников действий контроллера.
Файл-загрузчик
/** Loading application from session */ require_once 'Zend/Session/Namespace.php'; $application = new Zend_Session_Namespace('myApplication'); if (!isset($application->loggedUser)) { $application->loggedUser = null; } /** Setting up the front controller */ require_once 'Zend/Controller/Front.php'; $front = Zend_Controller_Front::getInstance(); $front->setControllerDirectory('path/to/controllers'); /** Registering the Plugin object */ require_once 'Zend/Controller/Plugin/Acl.php'; $front->registerPlugin(new Zend_Controller_Plugin_Acl($myAcl, $application->currentRole)); /** Registering the Action Helper object */ require_once 'Zend/Controller/Action/Helper/Acl.php'; require_once 'Zend/Controller/Action/HelperBroker.php'; Zend_Controller_Action_HelperBroker::addHelper(new Zend_Controller_Action_Helper_Acl()); /** Dispatching the front controller */ $front->dispatch();
И после регистрации помощника, мы можем использовать его внутри любого контроллера. Давайте дадим права доступа на изменение только для владельца или любого администратора.
Контроллер статей
class ArticleController extends Zend_Controller_Action { protected $_acl; protected $_application; public function init() { /** Get our Action Helper */ $this->_acl = $this->_helper->getHelper('acl'); require_once 'Zend/Session/Namespace.php'; $this->_application = new Zend_Session_Namespace('myApplication'); } ... public function editAction() { /** Load article by id */ $article = new Article($this->_request->id); /** Validate if the user is the owner or an Admin */ if (($article->author != $this->_application->loggedUser) && ($this->_application->currentRole != 'admin')) { $this->_acl->denyAccess(); } ... } }
Заключение
Некоторые любители копошиться в мусоре всю свою жизнь пытаются найти недостающее звено (и они так и умрут в поиске его) и некоторые ЗФ-еры состарятся в попытках заставить работать ACL должным образом в их MVC-окружении. Надеюсь, высказанное выше предложение, может оказаться одним из недостающих кусочков в мире ACL + MVC.
В заключении хочу дать рекомендацию. Придерживайтесь принципа «Делай проще»: если вам не нужна позарез динамическая загрузка ACL, то его загрузка и настройка вручную — совсем не грех, возможно это лучший способ действий в данной ситуации.
Для более подробного ознакомления с темой вы можете почитать следующее:
Zend_Acl & MVC Integration
и небольшой пример реализации подхода, описанного в статье:
Source Code
Crosspost: http://lobach.info/develop/zf/zend_acl-and-mvc-integration-part-2/