Zend Framework Workshop. Part One: Authentication and Acl


    Recently, I am becoming more and more convinced of the versatility of the Zend Framework as a platform for creating web applications. Today I will talk about the process of creating a site framework on the Zend Framework, which will provide the necessary basis for the implementation of sites of medium complexity:
    • Part one
      • Authentication - user login
      • ACL - distribution of access rights

    • Part two
      • Routing - setting url for various system components
      • Registry - quick access to system constants

    Authentication


    Authentication is an integral part of almost any site. As you know, the Zend_Auth component is used in the Zend Framework for these purposes. This component allows you to perform the login process by checking if the login-password pair matches. Typically, the entry point is a special authentication action, as well as an action registration (or registration confirmation) is possible. Consider a simple authentication example:
    public function authAction(){
            $form = new Application_Form_Enter();
            if ($form->isValid($this->getRequest()->getPost())){
                $bootstrap = $this->getInvokeArg('bootstrap');
                $auth = Zend_Auth::getInstance();
                $adapter = $bootstrap->getPluginResource('db')->getDbAdapter();
                $authAdapter = new Zend_Auth_Adapter_DbTable(
                                                           $adapter, 'user', 'login', 
                                                           'password', 'MD5(?)'
                                                      );
                $authAdapter->setIdentity($form->login->getValue());
                $authAdapter->setCredential($form->password->getValue());
                $result = $auth->authenticate($authAdapter);
                // Если валидация прошла успешно сохраняем в storage инфу о пользователе
                if ($result->isValid()){
                    $storage = $auth->getStorage();
                    $storage_data = $authAdapter->getResultRowObject(
                                             null, 
                                             array('activate', 'password', 'enabled'));
                    $user_model = new Application_Model_DbTable_User();
                    $language_model = new Application_Model_DbTable_Language();
                    $storage_data->status = 'user';
                    $storage->write($storage_data);
                }
        }
    }
    

    This is a typical authentication code that you can use. For full work you will need the appropriate form, which sends the username and password to this action.
    After successful authentication, user data is stored in Zend_Auth storage. Further, when you need to find out some information about the current user, you can turn to Zend_Auth (it is available everywhere, as it is a Singleton implementation) as follows:
    $auth = Zend_Auth::getInstance();
    // Если пользователь аутентифицирован
    if ($auth->hasIdentity()){
        // Считываем данные о пользователе
        $user_data = $auth->getStorage()->read();
    }
    

    Another important step that needs to be performed is the initial initialization of Zend_Auth when the user first visits the site. To do this, add the following method to bootstrap:
    public function _initAuth(){
        $auth = Zend_Auth::getInstance();
        $data = $auth->getStorage()->read();
        if (!isset($data->status)){
            $storage_data = new stdClass();
            $storage_data->status = 'guest';
            $auth->getStorage()->write($storage_data);
        }
    }
    

    Acl


    Most web applications have several access statuses, each of which has certain privileges. For most sites, privileges and their distribution are relatively constant, so we implement Acl in the form of rules written directly in the program code. If you are developing a system that has a frequently changing structure of statuses and rights (for example, CMS), then you need to build a more flexible implementation of Acl, the rights in which will be stored, for example, in the database.
    The main tasks that an access control system must perform are setting privileges and actually controlling access. To implement these tasks, we need two components:
    • Acl - Authorization Lists
    • Plugin that checks the correct access of the current user to the requested resource

    Consider the simplest case when resources are parts of a site, i.e. in terms of MVC, actions. Each user inherits rights from a certain abstract status (guest, user, administrator), the privileges of each status are described in Acl. To implement Acl, we extend Zend_Acl:
    class Acl extends Zend_Acl {
        public function  __construct() {
            //Добавляем роли
            $this->addRole('guest');
            $this->addRole('user', 'guest');
            $this->addRole('admin', 'user');
            //Добавляем ресурсы
            // РЕСУРСЫ ГОСТЯ !
            $this->add(new Zend_Acl_Resource('guest_allow'));
            $this->add(new Zend_Acl_Resource('index/index'),'guest_allow');
            //...
            // РЕСУРСЫ ПОЛЬЗОВАТЕЛЯ !
            $this->add(new Zend_Acl_Resource('user_allow'));
            $this->add(new Zend_Acl_Resource('user/index'), 'user_allow');
            // ...
            // РЕСУРСЫ АДМИНА !
            $this->add(new Zend_Acl_Resource('admin_allow'));
            $this->add(new Zend_Acl_Resource('admin/index'), 'admin_allow');
            //...        
            //Выставляем права, по-умолчанию всё запрещено
            $this->deny(null, null, null);
            $this->allow('guest', 'guest_allow', 'show');
            $this->allow('user', 'user_allow', 'show');
            $this->allow('admin','admin_allow', 'show');
        }
        public function can($privilege='show'){
            //Инициируем ресурс
            $request = Zend_Controller_Front::getInstance()->getRequest();
            $resource = $request->getControllerName() . '/' . $request->getActionName();
            //Если ресурс не найден закрываем доступ
            if (!$this->has($resource))
                return true;
            //Инициируем роль
            $storage_data = Zend_Auth::getInstance()->getStorage()->read();
            $role = array_key_exists('status', $storage_data)?$storage_data->status : 'guest';
            return $this->isAllowed($role, $resource, $privilege);
        }
    }
    

    Place this code in the application / classes / Acl.php file.
    We describe the lists of rights in the standard form for ZF. A method is also created here that checks the access rights of the current user for the current action. As resource identifiers, the controller / action format is used. If you design the system in such a way that your rights of action inside the controller do not change, then you can use only the names of the controllers instead of resource identifiers (do not forget to change the method can).
    For greater flexibility, we add the concept of "privilege", which will allow you to control certain actions within the action. The privilege for viewing is called "show."
    Now that we have a list of access rights and we can determine whether the user has access to the current action, we need to implement the check in the Zend Framework request processing cycle. Creating a front controller plugin is best suited for this. Plugins allow you to perform specified actions at various stages of the dispatching process:
    class CheckAccess extends Zend_Controller_Plugin_Abstract {
        /**
         * Метод preDispatch выполняет проверку прав доступа на
         * данный controller/action в случае ошибки вызывает метод
         * generateAccessError
         * 
         * @param Zend_Controller_Request_Abstract $request
         */
        public function  preDispatch(Zend_Controller_Request_Abstract $request) {
            $acl = Zend_Controller_Front::getInstance()->getParam('bootstrap')->getResource('Acl');
            if (!$acl->can()){
                $this->generateAccessError();
            }
        }
        /**
         * Метод генерации сообщения о ошибке прав доступа.
         * Выполняет перенаправление на контроллер error и выводит в нём
         * сообщение о ошибке передаваемое в метод.
         * 
         * @param string $msg
         */
        public function generateAccessError($msg='Доступ запрещён!'){
            $request = $this->getRequest();
            $request->setControllerName ('error');
            $request->setActionName('error');
            $request->setParam('message', $msg);
        }
    }
    

    Place this code in the application / plugins / CheckAccess.php file.
    This plugin will perform a user access check every time a request arrives at the site. To verify access, the Acl class described above is used. In case of an error, the request will be focused on error / error. In order for the error message to be correctly displayed, you need to add the appropriate code to ErrorController.php.
    Now you need to connect the plugin and create an Acl resource in bootstrap:
    public function _initAcl(){
        Zend_Loader::loadClass('Acl');
        Zend_Loader::loadClass('CheckAccess');
        Zend_Controller_Front::getInstance()->registerPlugin(new CheckAccess());
        return new Acl();
    } 
    

    In order for Zend_Loader to “know” where to look for our files, add it to application.ini:
    includePaths.plugins = APPLICATION_PATH "/ plugins"
    includePaths.classes = APPLICATION_PATH "/ classes"

    PS in this post we consider only one of the ways to implement access control, which is not universal, but satisfies the needs of most small sites

    Also popular now: