Pull to refresh

Интеграция Zend_Acl и MVC. Часть 2 (продвинутый вариант использования)

Reading time 6 min
Views 1.4K
Original author: Aldemar Bernal
На DevZone появилась вторая часть статьи о интреграции Zend_Acl в MVC Альдемара Бернала и я спешу предложить вам ее перевод.

В первой части мы говорили о том, как настроить экземпляр Zend_Acl и включить его в окружение MVC (с использованием плагина фронт-контроллера). Но как же настройки других действий для блокирования доступа, или как сделать редактирование статьи только её автором? Это и кое-что еще будет рассмотрено далее.



Как я уже говорил в первой части, эта статья основана на следующем предложении, которое в настоящий момент находится в стадии исследования.

1. Использование модулей


Давайте поговорим о модулях. В качестве примера для данной статьи мы взяли сайт подобный ДевЗоне. А что если мы создадим административный модуль, задачей которого будет реализация процедуры одобрения статей. Кроме того в модуле можно реализовать еще ряд задач, таких как управление короткими ссылками или категориями. В этом случае нам придется изменить нашу модель ресурсов:

Общий модуль
  1. Контроллер пользователей.
  2. Контроллер статей.

Административный модуль
  1. Контроллер статей.
  2. Контроллер коротких ссылок.
  3. Контроллер категорий.

Основываясь на этой новой модели ресурсов, мы создадим экземпляр 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/
Tags:
Hubs:
+1
Comments 5
Comments Comments 5

Articles