Practical Zend_ACL + Zend_Auth implementation and best practices Practical Zend_ACL + Zend_Auth implementation and best practices php php

Practical Zend_ACL + Zend_Auth implementation and best practices


My implementation:

Question #1

class App_Model_Acl extends Zend_Acl{       const ROLE_GUEST        = 'guest';    const ROLE_USER         = 'user';    const ROLE_PUBLISHER    = 'publisher';    const ROLE_EDITOR       = 'editor';    const ROLE_ADMIN        = 'admin';    const ROLE_GOD          = 'god';    protected static $_instance;    /* Singleton pattern */    protected function __construct()    {        $this->addRole(new Zend_Acl_Role(self::ROLE_GUEST));        $this->addRole(new Zend_Acl_Role(self::ROLE_USER), self::ROLE_GUEST);        $this->addRole(new Zend_Acl_Role(self::ROLE_PUBLISHER), self::ROLE_USER);        $this->addRole(new Zend_Acl_Role(self::ROLE_EDITOR), self::ROLE_PUBLISHER);        $this->addRole(new Zend_Acl_Role(self::ROLE_ADMIN), self::ROLE_EDITOR);        //unique role for superadmin        $this->addRole(new Zend_Acl_Role(self::ROLE_GOD));        $this->allow(self::ROLE_GOD);        /* Adding new resources */        $this->add(new Zend_Acl_Resource('mvc:users'))             ->add(new Zend_Acl_Resource('mvc:users.auth'), 'mvc:users')             ->add(new Zend_Acl_Resource('mvc:users.list'), 'mvc:users');        $this->allow(null, 'mvc:users', array('index', 'list'));        $this->allow('guest', 'mvc:users.auth', array('index', 'login'));        $this->allow('guest', 'mvc:users.list', array('index', 'list'));        $this->deny(array('user'), 'mvc:users.auth', array('login'));        /* Adding new resources */        $moduleResource = new Zend_Acl_Resource('mvc:snippets');        $this->add($moduleResource)             ->add(new Zend_Acl_Resource('mvc:snippets.crud'), $moduleResource)             ->add(new Zend_Acl_Resource('mvc:snippets.list'), $moduleResource);        $this->allow(null, $moduleResource, array('index', 'list'));        $this->allow('user', 'mvc:snippets.crud', array('create', 'update', 'delete', 'read', 'list'));        $this->allow('guest', 'mvc:snippets.list', array('index', 'list'));        return $this;    }    protected static $_user;    public static function setUser(Users_Model_User $user = null)    {        if (null === $user) {            throw new InvalidArgumentException('$user is null');        }        self::$_user = $user;    }    /**     *      * @return App_Model_Acl     */    public static function getInstance()    {        if (null === self::$_instance) {            self::$_instance = new self();        }        return self::$_instance;    }    public static function resetInstance()    {        self::$_instance = null;        self::getInstance();    }}class Smapp extends Bootstrap // class Bootstrap extends Zend_Application_Bootstrap_Bootstrap{    /**     * @var App_Model_User     */    protected static $_currentUser;    public function __construct($application)    {        parent::__construct($application);    }    public static function setCurrentUser(Users_Model_User $user)    {        self::$_currentUser = $user;    }    /**     * @return App_Model_User     */    public static function getCurrentUser()    {        if (null === self::$_currentUser) {            self::setCurrentUser(Users_Service_User::getUserModel());        }        return self::$_currentUser;    }    /**     * @return App_Model_User     */    public static function getCurrentUserId()    {        $user = self::getCurrentUser();        return $user->getId();    }}

in class bootstrap

protected function _initUser(){    $auth = Zend_Auth::getInstance();    if ($auth->hasIdentity()) {        if ($user = Users_Service_User::findOneByOpenId($auth->getIdentity())) {            $userLastAccess = strtotime($user->last_access);            //update the date of the last login time in 5 minutes            if ((time() - $userLastAccess) > 60*5) {                $date = new Zend_Date();                $user->last_access = $date->toString('YYYY-MM-dd HH:mm:ss');                $user->save();            }            Smapp::setCurrentUser($user);        }    }    return Smapp::getCurrentUser();}protected function _initAcl(){    $acl = App_Model_Acl::getInstance();    Zend_View_Helper_Navigation_HelperAbstract::setDefaultAcl($acl);    Zend_View_Helper_Navigation_HelperAbstract::setDefaultRole(Smapp::getCurrentUser()->role);    Zend_Registry::set('Zend_Acl', $acl);    return $acl;}

and Front_Controller_Plugin

class App_Plugin_Auth extends Zend_Controller_Plugin_Abstract{    private $_identity;    /**     * the acl object     *     * @var zend_acl     */    private $_acl;    /**     * the page to direct to if there is a current     * user but they do not have permission to access     * the resource     *     * @var array     */    private $_noacl = array('module' => 'admin',                             'controller' => 'error',                             'action' => 'no-auth');    /**     * the page to direct to if there is not current user     *     * @var unknown_type     */    private $_noauth = array('module' => 'users',                             'controller' => 'auth',                             'action' => 'login');    /**     * validate the current user's request     *     * @param zend_controller_request $request     */    public function preDispatch(Zend_Controller_Request_Abstract $request)    {        $this->_identity = Smapp::getCurrentUser();        $this->_acl = App_Model_Acl::getInstance();        if (!empty($this->_identity)) {            $role = $this->_identity->role;        } else {            $role = null;        }        $controller = $request->controller;        $module = $request->module;        $controller = $controller;        $action = $request->action;        //go from more specific to less specific        $moduleLevel = 'mvc:'.$module;        $controllerLevel = $moduleLevel . '.' . $controller;        $privelege = $action;        if ($this->_acl->has($controllerLevel)) {            $resource = $controllerLevel;        } else {            $resource = $moduleLevel;        }        if ($module != 'default' && $controller != 'index') {            if ($this->_acl->has($resource) && !$this->_acl->isAllowed($role, $resource, $privelege)) {                if (!$this->_identity) {                    $request->setModuleName($this->_noauth['module']);                    $request->setControllerName($this->_noauth['controller']);                    $request->setActionName($this->_noauth['action']);                    //$request->setParam('authPage', 'login');                } else {                   $request->setModuleName($this->_noacl['module']);                   $request->setControllerName($this->_noacl['controller']);                   $request->setActionName($this->_noacl['action']);                   //$request->setParam('authPage', 'noauth');               }               throw new Exception('Access denied. ' . $resource . '::' . $role);            }        }    }}

and finnaly - Auth_Controller` :)

class Users_AuthController extends Smapp_Controller_Action {       //sesssion    protected $_storage;    public function getStorage()    {        if (null === $this->_storage) {            $this->_storage = new Zend_Session_Namespace(__CLASS__);        }        return $this->_storage;    }    public function indexAction()    {        return $this->_forward('login');    }    public function loginAction()    {           $openId = null;        if ($this->getRequest()->isPost() and $openId = ($this->_getParam('openid_identifier', false))) {            //do nothing        } elseif (!isset($_GET['openid_mode'])) {            return;         }        //$userService = $this->loadService('User');        $userService = new Users_Service_User();        $result = $userService->authenticate($openId, $this->getResponse());        if ($result->isValid()) {            $identity = $result->getIdentity();            if (!$identity['Profile']['display_name']) {                return $this->_helper->redirector->gotoSimpleAndExit('update', 'profile');            }            $this->_redirect('/');        } else {            $this->view->errorMessages = $result->getMessages();        }    }    public function logoutAction()    {        $auth = Zend_Auth::getInstance();        $auth->clearIdentity();        //Zend_Session::destroy();        $this->_redirect('/');    }}

Question #2

keep it inside Zend_Auth.

after succesfull auth - write identity in storage. $auth->getStorage()->write($result->getIdentity());

the identity - is simply user_id

DB design

CREATE TABLE `user` (  `id` bigint(20) NOT NULL AUTO_INCREMENT,  `open_id` varchar(255) NOT NULL,  `role` varchar(20) NOT NULL,  `last_access` datetime NOT NULL,  `created_at` datetime NOT NULL,  PRIMARY KEY (`id`),  UNIQUE KEY `open_id` (`open_id`)) ENGINE=InnoDB  DEFAULT CHARSET=utf8CREATE TABLE `user_profile` (  `user_id` bigint(20) NOT NULL,  `display_name` varchar(100) DEFAULT NULL,  `email` varchar(100) DEFAULT NULL,  `real_name` varchar(100) DEFAULT NULL,  `website_url` varchar(255) DEFAULT NULL,  `location` varchar(100) DEFAULT NULL,  `birthday` date DEFAULT NULL,  `about_me` text,  `view_count` int(11) NOT NULL DEFAULT '0',  `updated_at` datetime NOT NULL,  PRIMARY KEY (`user_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;

some sugar

/** * SM's code library *  * @category     * @package      * @subpackage   * @copyright   Copyright (c) 2009 Pavel V Egorov * @author      Pavel V Egorov * @link        http://epavel.ru/ * @since       08.09.2009 */class Smapp_View_Helper_IsAllowed extends Zend_View_Helper_Abstract{    protected $_acl;    protected $_user;    public function isAllowed($resource = null, $privelege = null)    {        return (bool) $this->getAcl()->isAllowed($this->getUser(), $resource, $privelege);    }    /**     * @return App_Model_Acl     */    public function getAcl()    {        if (null === $this->_acl) {            $this->setAcl(App_Model_Acl::getInstance());        }        return $this->_acl;    }    /**     * @return App_View_Helper_IsAllowed     */    public function setAcl(Zend_Acl $acl)    {        $this->_acl = $acl;        return $this;    }    /**     * @return Users_Model_User     */    public function getUser()    {        if (null === $this->_user) {            $this->setUser(Smapp::getCurrentUser());        }        return $this->_user;    }    /**     * @return App_View_Helper_IsAllowed     */    public function setUser(Users_Model_User $user)    {        $this->_user = $user;        return $this;    }}

for things like this in any view script

 <?php if ($this->isAllowed('mvc:snippets.crud', 'update')) : ?>    <a title="Edit «<?=$this->escape($snippetInfo['title'])?>» snippet">Edit</a> <?php endif?>

Questions? :)