Practical Zend_ACL + Zend_Auth implementation and best practices

前端 未结 1 2132
清歌不尽
清歌不尽 2020-11-30 16:35

Context:

My questions pertain to a forum I\'m developing pretty much exactly like SO, where there are:

  1. guests who have access to view
相关标签:
1条回答
  • 2020-11-30 17:20

    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=utf8
    
    CREATE 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 &laquo;<?=$this->escape($snippetInfo['title'])?>&raquo; snippet">Edit</a>
     <?php endif?>
    

    Questions? :)

    0 讨论(0)
提交回复
热议问题