2010-01-12 7 views
42

Contexto:práctica aplicación Zend_Acl + Zend_Auth y las mejores prácticas

Mis preguntas se refieren a un foro que estoy desarrollando casi exactamente como tal, donde hay:

  1. los clientes que tienen acceso a ver hilos pero no puede responder ni votar
  2. miembros que, con suficientes representantes, pueden editar/votar otros hilos, y de forma predeterminada pueden responder y tener los mismos privilegios que los invitados
  3. administradores que pueden usar m Haga cualquier cosa

Me gustaría que esta ACL se aplicara en todo el sitio, y denegará por defecto todos los recursos.

Leí los conceptos básicos del uso de Zend_Acl, básicamente creando roles (invitado, miembro, administrador) y denegando o permitiendo recursos (controladores, métodos) a esos roles. La documentación no es muy específica de cómo en realidad se debe aplicar el código ACL en su aplicación, por lo que fue a buscar el SO ..

se encontró con un stackoverflow muy útil answer from marek la que arroja algo de luz sobre el tema, sin embargo debido a mi falta de familiaridad, todavía no puedo entender completamente cómo implementar esto correctamente con las mejores prácticas en mente.

El cartel tiene un archivo estático configAcl.php en la raíz de la aplicación que inicializa el objeto ACL, añade papeles, crea un recurso de cada controlador, da admin acceso a todo, da normal acceso a todo, pero el administrador y almacena el acl objeto en el registro para su uso posterior.

$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); 

Pregunta # 1 - En caso de que el código sea en el arranque, o en un archivo independiente como ésta? Si es así, ¿sería mejor si estuviera dentro, por ejemplo, el directorio de la biblioteca?

La segunda parte es una nueva clase que amplía la clase Zend Controller Plugin Abstract que permite engancharla en auth/login, la lógica es básicamente si el inicio de sesión falla, redirige ... de lo contrario, toma el objeto acl del registro, capta la identidad y determina si el usuario puede ver este recurso.

$identity = $auth->getIdentity(); 

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

Pregunta # 2 - ¿Cómo funciona exactamente iba a codificar la parte del complemento auth que realmente devuelve la identidad del usuario? Me di cuenta de que tenía un código debajo que generaba un objeto Auth adapter db table que consultaba la columna de la tabla de la base de datos por ID de usuario y credencial (verificación de paso hash). Estoy confundido sobre dónde encaja esto con la parte getIdentity.

Digamos que mi tabla de usuarios se compone de estos datos:

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

Cuando el nivel 3 = admin, 2 = miembro, 1 = invitado.

Pregunta # 3 - ¿dónde exactamente es un buen lugar para poner el código de autorización anterior? ¿Dentro del controlador de inicio de sesión?

Pregunta # 4 - otro cartel replies con su artículo sobre cómo la lógica ACL debe hacerse dentro de los modelos, sin embargo, el método específico que utiliza no es compatible de forma nativa y requiere una solución, es esto posible? ¿Y es así como idealmente debería hacerse?

Respuesta

75

Mi aplicación:

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

} 

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

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

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

Pregunta # 2

mantenerlo dentro Zend_Auth.

después de la autenticación exitosa: escriba la identidad en el almacenamiento. $auth->getStorage()->write($result->getIdentity());

la identity - es simplemente el diseño user_id

DB

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; 

un poco de azúcar

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

} 

para cosas como esta en cualquier script de vista

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

¿Preguntas? :)

+0

Impresionante, te agradezco el tiempo que tardaste en pegar todo eso. Trataré de implementarlo y te lo haré saber, gracias de nuevo. –

+0

escribir correo electrónico. (google, solo) :) – SMka

+0

bien, esto es mucho mejor de lo que he pensado. Sin embargo, una pregunta. ¿Por qué el objeto acl en App_Plugin_Auth no se recupera del registro donde lo has ubicado? gracias por la publicación –

Cuestiones relacionadas