2010-01-12 4 views
42

Contexte:Mise en œuvre pratique Zend_Acl + Zend_Auth et les meilleures pratiques

Mes questions portent sur un forum que je développe à peu près exactement comme SO, où il y a:

  1. clients qui ont accès à voir les discussions mais ne peut pas répondre ou voter
  2. membres qui, avec suffisamment de rep, peuvent modifier/voter d'autres discussions, et par défaut, ils peuvent répondre et avoir les mêmes privilèges que les invités
  3. admins qui peut j uch do anything

Je voudrais que cette ACL soit appliquée à l'ensemble du site et, par défaut, rejette toutes les ressources. Je lis les bases de l'utilisation de Zend_Acl - en ce sens que vous créez essentiellement des rôles (invité, membre, administrateur) et que vous refusez ou autorisez des ressources (contrôleurs, méthodes) à ces rôles. La documentation est pas très précis sur la façon dont vous devriez effectivement mettre en œuvre le code acl dans votre application, donc je suis allé à la recherche sur le SO ..

est tombé sur une stackoverflow très utile answer from marek qui jette une certaine lumière sur la question, mais En raison de mon manque de familiarité, je n'arrive toujours pas à comprendre comment mettre en œuvre correctement les meilleures pratiques.

L'affiche a un fichier statique configAcl.php dans la racine de l'application qui initialise l'objet acl, ajoute rôles, crée une ressource de chaque contrôleur, donne admin accès à tout, donne normal accès à tout, mais l'administrateur et stocke les acl objet dans le registre pour une utilisation ultérieure.

$acl = new Zend_Acl(); 

$roles = array('admin', 'normal'); 

// Controller script names. You have to add all of them if credential check 
// is global to your application. 
$controllers = array('auth', 'index', 'news', 'admin'); 

foreach ($roles as $role) { 
    $acl->addRole(new Zend_Acl_Role($role)); 
} 
foreach ($controllers as $controller) { 
    $acl->add(new Zend_Acl_Resource($controller)); 
} 

// Here comes credential definiton for admin user. 
$acl->allow('admin'); // Has access to everything. 

// Here comes credential definition for normal user. 
$acl->allow('normal'); // Has access to everything... 
$acl->deny('normal', 'admin'); // ... except the admin controller. 

// Finally I store whole ACL definition to registry for use 
// in AuthPlugin plugin. 
$registry = Zend_Registry::getInstance(); 
$registry->set('acl', $acl); 

Question n ° 1 - Si ce code dans l'amorçage, ou dans un fichier autonome comme celui-ci? Si oui serait-il préférable si c'était à l'intérieur dire, le répertoire de la bibliothèque?

La deuxième partie est une nouvelle classe qui étend la classe Zend Controller Plugin Abstract qui lui permet d'être connectée en auth/login, la logique est essentiellement si la connexion échoue, elle redirige .. sinon elle saisit l'objet acl depuis le Registre, saisit l'identité et détermine si l'utilisateur est autorisé à afficher cette ressource.

$identity = $auth->getIdentity(); 

$frontController->registerPlugin(new AuthPlugin()); 

Question # 2 - Comment exactement je code la partie plugin auth qui retourne en fait l'identité de l'utilisateur? Je me rends compte qu'il avait un code ci-dessous qui générait un objet table Auth adaptateur db qui interroge la colonne d'une table de base de données par identifiant et référence (hashed pass check). Je ne sais pas où cela correspond à la partie getIdentity.

Disons que ma table d'utilisateurs était composé de ces données:

user_id user_name level 
1   superadmin 3 
2   john   2 
3   example.com 1 

Quand le niveau 3 = admin, 2 = membre, 1 = invité.

Question # 3 - où est exactement un bon endroit pour mettre le code d'autorisation ci-dessus? À l'intérieur du contrôleur de connexion?

Question n ° 4 - une autre affiche replies avec son article sur la façon dont la logique devrait acl se faire à l'intérieur des modèles, mais la méthode spécifique qu'il utilise est pas pris en charge en mode natif et nécessite une solution de contournement, est-ce possible? Et est-ce vraiment comment cela devrait idéalement être fait?

Répondre

75

Ma mise en œuvre:

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(); 
    } 

} 

dans 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; 
} 

et 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); 
      } 
     } 
    } 
} 

et finnaly - Auth_Cont roller` :)

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

garder à l'intérieur Zend_Auth.

après une authentification authentifiée réussie dans le stockage. $auth->getStorage()->write($result->getIdentity());

le identity - est tout simplement user_id

DB conception

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; 

sucre

/** 
* 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; 
    } 

} 

pour des choses comme cela dans un script de vue

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

Questions? :)

+0

Génial, j'apprécie le temps que vous avez pris pour coller tout cela. Je vais essayer de l'implémenter et vous le faire savoir, merci encore. –

+0

écrivez un courriel. (google, seulement) :) – SMka

+0

ok c'est bien mieux que ce que j'ai pensé.une question cependant. pourquoi l'objet acl dans le App_Plugin_Auth n'est pas récupéré dans le registre où vous l'avez mis? merci pour le poste –

Questions connexes