Contexto:

Mi pregunta es con respecto a un foro que estoy desarrollando casi exactamente igual ASÍ que, donde hay:

  1. los huéspedes que tengan acceso para ver los hilos, pero no pueden responder ni voto
  2. miembros que, con suficiente rep, puede editar/voto en otros hilos, y por defecto se puede responder y tienen los mismos privilegios que los huéspedes
  3. administradores que puede hacer casi cualquier cosa

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

He leído los fundamentos de la utilización de Zend_Acl – en la que, básicamente, crear roles ( invitado, miembro admin ) y denegar o permitir los recursos ( controladores, métodos ) de esos roles. La documentación no es muy específico acerca de cómo en realidad se debe aplicar la acl de código en su aplicación, por lo que me puse a buscar en LA..

Llegó a través de un programa muy útil, stackoverflow respuesta de marek que arroja algo de luz sobre el tema, sin embargo debido a mi desconocimiento todavía no puedo plenamente grok cómo implementar correctamente esta con las mejores prácticas en la mente.

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

$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 – Debe este código en el bootstrap, o en un archivo independiente como este? Si es así sería mejor si era en decir, el directorio de la biblioteca?

La segunda parte es una nueva clase de ampliar el Zend Controlador Plugin clase Abstracta que permite ser conectado a auth/login, la lógica es, básicamente, si el error de inicio de sesión, se le redirige.. de lo contrario, se agarra a la acl objeto del registro, se agarra a la identidad, y determina si el usuario tiene permiso para ver este recurso.

$identity = $auth->getIdentity();

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

Pregunta #2 – ¿exactamente Cómo iba yo a código auth plugin parte que realmente devuelve la identidad del usuario? Me doy cuenta de que había algo de código a continuación que genera un Auth adaptador db objeto de tabla que se consulta una tabla de base de datos de la columna de id de usuario y la credencial ( hash pasar de verificación ).. estoy confundido sobre dónde esto encaja con la getIdentity parte.

Digamos que mi tabla de usuarios se compone de los siguientes datos:

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

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

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

Pregunta #4 – otro cartel respuestas con su artículo sobre cómo la acl lógica debe ser hecho dentro de los modelos, sin embargo, el método específico que se utiliza no es compatible de forma nativa y requiere una solución, es esto posible? Y es esta realmente la forma en la que idealmente se debe hacer?

1 Comentario

  1. 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 finalmente – 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('/');
    }
    }

    Pregunta #2

    mantendrá dentro de Zend_Auth.

    después de exito auth – escritura de la identidad en el almacenamiento. $auth->getStorage()->write($result->getIdentity());

    la identity – es simplemente user_id

    DB diseño

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

    por 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? 🙂

    • Impresionante, yo agradezco el tiempo que tomó para pegar todo eso. Voy a tratar de implementarlo y hacerle saber, gracias de nuevo.
    • escribir mensajes de correo electrónico. (google, solamente) 🙂
    • aceptar esto es mucho mejor que lo que he pensado.una pregunta, sin embargo. ¿por qué la acl objeto en el App_Plugin_Auth no se recupera desde el registro en el que te he puesto? gracias por el post
    • con singleton no es importante de donde obtener la ACL
    • En qué archivo debe el Smapp clase se van?
    • Lo que en el mundo es Smapp_Controller_Action?
    • Smapp_Controller_Action extender Zend_Controller_Action
    • Smapp es mi personal lib prefijo. cambiar el nombre que usted necesita
    • Oye, esto es muy útil, gracias. La única cosa que no me queda claro es la Smapp clase. Es utilizado en el contexto del objeto? Cuando se crea una instancia? En lugar de la habitual de bootstrap, tal vez?
    • Smapp es el nombre de su aplicación. De forma predeterminada, el nombre de una Zend aplicación es Default.
    • class Smapp extends Bootstrap // class Bootstrap extends Zend_Application_Bootstrap_Bootstrap y bootstrap.path = APPLICATION_PATH "/ApplicationBootstrap.php" bootstrap.class = "Smapp" en application.ini archivo de configuración. ahora es el momento de probar ZF2 🙂
    • Hm. Lo que si tengo mis papeles en la base de datos?
    • escribir algo de código a ti mismo 😉
    • 2+ años de pregunta. ZF2 en RC3 ya 🙂
    • Upvote sólo por el esfuerzo que en esta respuesta, sólo estoy investigar las mejores prácticas para la ACL ya que un proyecto va a requerir. Esto ha sido la penetración probado en forma alguna?

Dejar respuesta

Please enter your comment!
Please enter your name here