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

Перевод статьи «Zend_Acl and MVC Integration Part I (Basic Use)» Автор Aldemar Bernal

Итак, что не так с Zend_Acl и текущей реализацией MVC в Zend Framework? Ничего неправильного нет, просто не слишком очевидно для разработчиков, как достичь оптимальной интеграции между этими двумя важными частями фреймворка.

Во-первых, эта статья основана на следующем предложении (link), в настоящий момент находящемся в стадии Ожидания рекомендации.

Ну, как это работает? Существуют два основных компонента в этом предложении:

  1. Плагин фронт-контроллера (Front Controller Plugin): этот компонент решает, имеет ли доступ текущий пользователь к открываемой странице.
  2. Помощник действия (Action Helper): Этот компонент позволяет проверить, имеет ли текущий пользователь доступ внутрь контроллера.

Опираясь на эти два компонента, давайте попробуем их на примере. Давайте будем говорить о сайте, подобном DevZone.
Нам потребуется контроллер для управления пользователями и еще один контроллер для управления статьями, так же 3 типа пользователей (ролей): одну для гостей, одну для авторов статей и еще одну для утверждения статей. Итого, мы имеем:

Ресурсы:

  1. Контроллер пользователей.
  2. Контроллер статей.

Роли:

  1. Гость (Guest).
  2. Автор (Writer).
  3. Администратор (Admin).

Настройка компонента Zend_Acl

После определения того, что нам нужно сделать, следующим шагом будет создание экземпляра Zend_Acl, отражающего нашу модель.

/** Creating the ACL object */
require_once 'Zend/Acl.php';
$myAcl = new 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';
$myAcl->add(new Zend_Acl_Resource('user'))
      ->add(new Zend_Acl_Resource('article'));

Создание привилегий

Теперь мы добавили роли и ресурсы в наш экземпляр Zend_Acl, пора объяснить, какие действия должны быть доступны для каких ролей.

  1. Гости не могут редактировать, добавлять и публиковать статьи.
  2. Авторы не могут публиковать статьи.
  3. Администраторы имеют полный доступ.
/** Creating permissions */
$myAcl->allow('guest', 'user')
      ->deny('guest', 'article')
      ->allow('guest', 'article', 'view')
      ->allow('writer', 'article', array('add', 'edit'))
      ->allow('admin', 'article', 'approve');

Создание страницы, отображаемой при отсутствии доступа

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

class ErrorController extends Zend_Controller_Action
{
    ....

    public function deniedAction()
    {
    }
    ....
}

Затем мы создадим наш файл представления (/application/views/scripts/error/denied.phtml) с некоторым предупреждающим сообщением:

<h1>Error</h1>
<h2>Access denied</h2>
<p>You are trying to access an area which you have not allowed.</p>

Завершение настройки

Хорошо, мы настроили наш экземпляр Zend_Acl. Следующий шаг — регистрация плагина контроллера. Это важная часть берет созданный нами экземпляр Zend_Acl и проверяет, доступна ли текущая страница пользователю.

/** 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';
$aclPlugin = new Zend_Controller_Plugin_Acl($myAcl);
$aclPlugin->setRoleName($currentUserRole);

$front->registerPlugin(new Zend_Controller_Plugin_Acl($acl, 'guest')); 
/** Dispatching the front controller */
$front->dispatch();

После завершения настройки, как только пользователь войдет в наше приложение, в зависимости от его/её роли будет либо отображена запрошенная страница, либо страница с сообщением о запрете доступа.

Для более подробного ознакомления с темой вы можете почитать следующее:
Zend_Acl & MVC Integration

и небольшой пример:
Source Code

google.com bobrdobr.ru del.icio.us technorati.com linkstore.ru news2.ru rumarkz.ru memori.ru moemesto.ru
Print
1 звезда2 звезды3 звезды4 звезды5 звезд (2 голосов, средний: 1.00 из 5)
Loading ... Loading ...

Метки: , , ,



30 комментариев на “Интеграция Zend_Acl и MVC. Часть 1 (простейшее использование)”

  1. пишите полезную инфу, подберите скин для блога повеселее :)

  2. Лобач Олег пишет:

    «Полезную» это какую?

    А до скина уже с пол-года руки не доходят :(

  3. Масанов пишет:

    Скинов полно для вордпресса. Просто дефолтный уже всем приелся.

  4. Лобач Олег пишет:

    Вот закрою текущий проект и займусь версткой скина...

  5. [...] Лобач.info Заметки LAMP-программиста о вебдеве и не только… « Интеграция Zend_Acl и MVC. Часть 1 (простейшее использование) [...]

  6. tatarin пишет:

    Во первых когда скачал плагин и хелпер то они блин назывались не Zend_... а Zion_... пришлось все переименовывать,чтобы Zend_Loader::registerAutoload () работал((.Во вторых очень не нравиться то что для того чтобы обратиться к ЛЮБОМУ контроллеру(в понимании плогина как ресурса) приходится его объявлять как ресурс,а если скажем есть контроллер который всегда без всяких там прав должен открываться,ну хотя бы регистрация или еще что то там...Я поэтому решил скорректировать с децл код плагина (Zion_Controller_Plugin_Acl) — в методе preDispatch перед строчкой if (!$this->getAcl () ->isAllowed ($this->_roleName, $resourceName, $request->getActionName ())) поставил условие if ($this->getAcl () ->has ($resourceName)),чтобы проверить существует ли ресурс,теперь можно смело объявлять ресурсами только лишь те контроллеры на котроые действительно нужно контролить доступ.))))

  7. tatarin пишет:

    кстати,у меня возникла проблема-не перехватывается процесс дисперетчизации если я использую хелпер Zend_View action,то есть проблема вот какая в скрипте вида пишу action ('say', 'say'); ?>,на который скажем гостю запрещен доступ,а он все равно ставновится виден...помогите пожаласта-не пойму че делать,чтобы ему доступ ограничивало)))

  8. Лобач Олег пишет:

    @tatarin: честно говоря, не совсем понятно что у Вас работает не так, как надо.

    Может быть опишете более подробно в чем проблема и сделаете архив с кодом, чтобы можно было посмотреть?

  9. tatarin пишет:

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

    В общем задача такая,чтобы мой плагин обошел все вызываемые пути,которые надо обработать в данном вызове,при запуске фронт контроллера(метод preDispatch в плагине) и просто их вывел......

    У меня такая проблема-вызывается помощник action action ('say', 'say'); ?> в скрипте вида say/index.tpl,тока вот почему то фронт контроллер не воспринимает то что запрос идет к say/say (в нашем случае из помощника action) а не к say/index (контроллер/действие)...объясните почему это и как это обойти...или я че то путаю?Может тут поможет ActionStack-я правда ничего не знаю о нем,но название такое что он в себе содержит вроде все запросы которые обработались по одному маршруту(в моем случае их 2-say/index и say/say).

    Моя логика-он должен вывести по маршруту cs/say

    --say/index

    --say/say

    А выводит зараза совсем нито))

    Тут может еще preDispatch у всех контроллеров надо перегрузить,чтобы они ловили все вызовы к ним,но нельзя ли обойтись как нить без этого?

  10. tatarin пишет:

    Ну еще добавлю по проблеме:

    данный плагин позволяет отслеживать доступ только лишь к тому маршруту,который задается через урл(при инстанировании фронт контроллера),а теперь представьте себе ,что мы запросили контроллер и дейтсвие через урл к которому доступ открыт,а вот в виде контроллера вызывается хелпер action ('действие к которому нельзя получить доступ','контроллер к которому нельзя получить доступ') -так вот метод преддиспетчиризации в плагине не ловит этого и поэтому выводится содержимое запрещенного маршрута...поэтому в моей ситуации этот плагин оказался бесполезным(я роюсь уже 4 дня как все заствить это работать),так как вроде как он и не может ловить все маршруты вызываемые в процессе диспетчиризации...пришлось расширять класс Zend_Controller_Action и от него наследовать все остальные контроллеры...если вы разберетесь с этим и выложите идеи как ловить все маршруты то напишите,буду благодарен)))

  11. Лобач Олег пишет:

    Это ведь плагин контроллера. А для решения Вашей задачи может стоит написать (или расширить стандартный) вью хелпер?

  12. Лобач Олег пишет:

    пришлось расширять класс Zend_Controller_Action

    На мой взгляд вполне нормальное решение. Что в нем плохого?

  13. tatarin пишет:

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

    почему _forward в init () выдает ошибку

    string (2092) "ERROR: Action «denied» does not exist and was not

    trapped in __call ()

    0 Z:\home\cs\www\libs\Zend\Controller\Action.php (518):

    Zend_Controller_Action->__call ('deniedAction', Array)

    а если я его использую в init (),а если использую в preDispatch () то

    все работает как надо?

  14. tatarin пишет:

    если надо класс кстати могу выложить))

  15. Лобач Олег пишет:

    если надо класс кстати могу выложить))

    Ну, хуже от этого не будет ;)

  16. tatarin пишет:

    class MyAclController extends Zend_Controller_Action 
    {
        /**
         * Pre-dispatch routines
         *
         * Called before action method. If using class with
         * {@link Zend_Controller_Front}, it may modify the
         * {@link $_request Request object} and reset its dispatched flag in order
         * to skip processing the current action.
         *
         * @return void
         */
        public function preDispatch()
        {
            parent::preDispatch();     
            $myacl=Zend_Registry::get('myacl');
            $resourceName = '';
            $request=$this->getRequest();
            if ($request->getModuleName() != 'default') {
                $resourceName .= $request->getModuleName() . ':';
            }
            $resourceName .= $request->getControllerName();
            if($myacl->has($resourceName))
            if (!$myacl->isAllowed(AuthModel::get_my_role(), $resourceName, $request->getActionName())) {
             $setting_error_dostup_MVC=Zend_Registry::get('setting_error_dostup_MVC');
             $modul='default';
             $controller='error';
             $action='denied'; 
             $params=array();
             $index_arr=$request->getModuleName().":".$request->getControllerName().":".$request->getActionName();
             if(isset($setting_error_dostup_MVC[$index_arr]))
              { 
                     $arr=$setting_error_dostup_MVC[$index_arr];
                     if(isset($arr['modul']))$modul=$arr['modul'];
                     if(isset($arr['controller']))$controller=$arr['controller'];
                     if(isset($arr['action']))$action=$arr['action'];
                     if(isset($arr['params']))$params=array('err_acl'=>$arr['params']);
              }  
             $this->_forward($action,$controller,$modul,$params);
            }
        }
    }

  17. tatarin пишет:

    кстати он не так работает когда идет пересыл в хелпере action-он вообще ничего не показывает если доступ запрещен вместо перенаправления...я не знаю почему...помогите если кто понимает в этом че))я вот уже голову сломал пока думал...просто берет и ничего не показывает...

  18. >>“Полезную” это какую?

    Имел ввиду пИшите, не призыв а констатация факта. А обложка, то есть скин блога, не привлекательная.

  19. Лобач Олег пишет:

    Имел ввиду пИшите, не призыв а констатация факта.

    Я действительно понял фразу как призыв.

    А обложка, то есть скин блога, не привлекательная.

    Да мне и самому не очень нравится, но до верстки нормального шаблона все никак руки не доходят.

  20. Да мне и самому не очень нравится, но до верстки нормального шаблона все никак руки не доходят.

    Есть ведь масса других уже готовых шаблонов, скачал, залил, включил и может даже сразу работать :) в гугле очень быстро находятся www.google.com/search?hl=...оны+wordpress

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

  22. Dima пишет:

    Подскажите, какую нибудь статью, как бы это еще все к базе данных прикрепить

  23. Лобач Олег пишет:

    Подскажите, какую нибудь статью, как бы это еще все к базе данных прикрепить

    По подробней опишите задачу: что именно прикрепить к БД?

    • excalibur пишет:

      Интерфейс для администратора по управлению прав групп пользователей случайно не разрабатывали? Конечно связка Zend_Acl+Zend_Db должна быть обязательно, но нужно это еще грамотно спроектировать, а ссылка ниже на связку пока не очень во многом помогла.

  24. Dima пишет:

    Имел ввиду, есть как нибудь наиболее оптимальные способы подгружать Acl динамически

  25. Лобач Олег пишет:

    http://yandex.ru/yandsearch?text=zend_acl+db

    И первая же ссылка ведет на простейший пример реализации хранения ACL в БД — my.opera.com/zomg/blog/20...esources-in-a-db

    Ну а оптимальность того или иного решения зависит от конкретной задачи и условий

  26. Лобач Олег пишет:

    Кстати, в комментариях к той статье есть ссылка на готовый класс (наследник Zend_ACL), использующий БД для хранения правил — www.phpclasses.org/browse/package/4100.html

  27. Dima пишет:

    Спасибо, очень признателен.

  28. Лобач Олег пишет:

    Хорошо, если я смог помочь

Оставить комментарий