Tout d'abord, il est essentiel de bien comprendre ce schéma que j'ai déjà proposé dans mon article de le plugin d'initialisation et également de bien comprendre les plugins et helpers.

Composants

Nous allons travailler avec 4 composants bien distincts que je décris dans les chapitres suivants.

My_Acl

Cette classe, héritée de Zend_Acl, servira à charger les rôles, ressources et permissions tels qu'on les aura définis. Elle répond à la question "Comment configurer mes droits d'accès ?".

Je vous propose ci-dessous un exemple de classe qui permet la gestion de droit dans un fichier INI. Libre à vous d'imaginer comment charger ces droits depuis une base de données ou autres. Vous pouvez également définir les droits directement dans la classe, mais ça, je ne saurais vous le conseiller.

L'appel de la classe se fait lors de l'initialisation de votre application, dans le bootstrap, puis est passée en paramètre dans le plugin de gestion des accès (prochain chapitre) :

$acl_ini = '../config/acl.ini' ;
$acl     = new My_Acl_Ini($acl_ini) ;

Le fichier INI est construit ainsi :

[roles]

guest = null
member = null
admin = null

[ressources]

; définition des ressources dans le module par défaut
index    = null
login    = null
products = null
member = null

; définition des ressources dans le module de blog
blog_index = null

[guest]

allow.login    = null
allow.index    = null
allow.products = list,view
allow.member   = subscribe
allow.blog_index = list,view

; member hérite de guest
[member : guest]

; on interdit le login (le membre est déjà identifié)
deny.login   = null
allow.member = index,orders
allow.blog_index = comment

; admin hérite de member
[admin : member]

allow.admin = null

La section des roles contient... les rôles et après le signe = le rôle dont il hérite. Mais dans mon exemple, j'ai préféré gérer l'héritage par la structure de groupe proposée par Zend_Config_Ini.

La section des ressources est organisée de manière identique. Le nom de la ressource correspond au nom du contrôleur d'action. On gère également les modules en préfixant le nom du contrôleur par le nom du module. Le module par défaut n'est pas spécifié.

Ensuite, une section est créée pour chaque rôle afin de définir les accès sur leurs permissions sur les ressources. La permission est accordée (allow) ou non (deny). Après le signe = nous retrouvons les actions auxquelles le rôle a le droit d'accéder. "null" signifie qu'il a les droits d'accès sur la totalité des actions du contrôleur.

La classe que je vous propose initialise chacun des rôles sans aucun droit. Il est donc obligatoire de tous les définir. Ca peut paraitre rébarbatif mais ça évite bien des problèmes et trous de sécurité !

Je n'ai pas copié la totalité du code de la classe afin d'éviter la pollution de cette page. Vous pouvez télécharger le code complet ici.

My_Controller_Plugin_Auth

C'est ici une des parties intéressantes de cet article. La réponse à la question : "Où et quand je vais contrôler l'accès ?". Et la réponse est simple : avant le processus de dispatching de la requête. C'est à dire avant que le contrôleur d'action soit appelé mais après le routage de la requête. A ce moment nous connaissons le contrôleur appelé et l'action appelée. Nous pouvons toutefois encore rediriger la requête vers un autre contrôleur, si nécessaire.

Ce plugin est composé d'une méthode que je recopie ci-dessous :

/**
 * Vérifie les autorisations
 */
public function preDispatch(Zend_Controller_Request_Abstract $request)  {
  // is the user authenticated
  if ($this->_auth->hasIdentity()) {
    // yes ! we get his role
    $user = $this->_auth->getStorage()->read() ;
    $role = $user['role'] ;
  } else {
    // no = guest user
    $role = 'guest';
  }
  
  $module   = $request->getModuleName() ;
  $controller = $request->getControllerName() ;
  $action     = $request->getActionName() ;
  
  $front = Zend_Controller_Front::getInstance() ;
  $default = $front->getDefaultModule() ;
  
  // compose le nom de la ressource
  if ($module == $default)  {
    $resource = $controller ;
  } else {
    $resource = $module.'_'.$controller ;
  }

  // est-ce que la ressource existe ?
  if (!$this->_acl->has($resource)) {
    $resource = null;
  }
  
  // contrôle si l'utilisateur est autorisé
  if (!$this->_acl->isAllowed($role, $resource, $action)) {
    // l'utilisateur n'est pas autorisé à accéder à cette ressource
    // on va le rediriger
    if (!$this->_auth->hasIdentity()) {
      // il n'est pas identifié -> module de login
      $module    = self::FAIL_AUTH_MODULE ;
      $controller = self::FAIL_AUTH_CONTROLLER ;
      $action      = self::FAIL_AUTH_ACTION ;
    } else {
      // il est identifié -> error de privilèges
      $module    = self::FAIL_ACL_MODULE ;
      $controller = self::FAIL_ACL_CONTROLLER ;
      $action      = self::FAIL_ACL_ACTION ;
    }
  }

  $request->setModuleName($module) ;
  $request->setControllerName($controller) ;
  $request->setActionName($action) ;
}

Vous pouvez télécharger le code complet ici.

Ce plugin est initialisé dans votre bootstrap

  // $auth est une référence vers Zend_Auth (getInstance())
  // $acl a été défini dans le chapitre précédent
  $front->registerPlugin(new My_Controller_Plugin_Auth($acl)) ;

Simple comme bonjour ! Non ? :)

Je vous propose encore un petit schéma pour améliorer la compréhension du processus.

auth.jpg

My_Auth_Adapter

Je pense qu'il est inutile que je répète la documentation qu'on peut trouver sur le site Zend. En gros, elle répond à la question "Comment vérifier le login et mot de passe d'un utilisateur ?". Votre tâche consiste ici en la création de votre propre adaptateur d'authentification ou alors l'utilisation d'un adaptateur existant.

Pour résumer, cette classe est appelée dans une action "login" et elle sauve dans la session (ou ailleurs) l'autorisation d'accès. Je vous conseille vivement d'ajouter dans votre table d'utilisateurs une colonne "role" qui correspondra à un des rôles que nous avons défini précédemment (guest, member ou admin).

My_View_Helper_IsAllowed

Cette aide de vue (Zend_View_Helper_Abstract) permet simplement d'appeler une fonction isAllowed('controller', 'action') avec en premier paramètre le contrôleur et en second l'action sur laquelle on désire effectuer le contrôle de permission.

Vous aurez certainement besoin de cette fonction, par exemple, pour faire apparaître les bons liens sur votre site, en fonction des droits de l'utilisateur.

/**
 * Proxy vers le contrôleur d'authentification
**/
class My_View_Helper_IsAllowed extends Zend_View_Helper_Abstract {
  protected $_acl;
  protected $_auth;
  
  /**
   * Proxy vers la fonction acl is allowed function
   */
  public function isAllowed($resource, $actions) {
    $this->_acl = Zend_Registry::get('acl') ;
    $this->_auth = Zend_Auth::getInstance() ;
    
    // contrôle si l'utilisateur est authentifié
    if ($this->_auth->hasIdentity()) {
      // yes ! on récupère son role
      $user = $this->_auth->getStorage()->read() ;
      $role = $user['role'] ;
    } else {
      // non = invité
      $role = 'guest';
    }
    
    // contrôle si l'utilisateur est autorisé
    return $this->_acl->isAllowed($role, $resource, $actions) ;
  }
}

Il est possible de définir d'autres aides de vue tout autant utiles. Celle-ci n'est qu'un exemple. Je pense qu'on peut dire qu'à partir du moment où dans un script de vue, on instancie une classe ou une variable du registre, il faut faire une aide de vue.

Conclusion

Voici donc tous les composants réunis pour gérer les accès dans votre site de manière simple et propre. Bien entendu, comme toujours, je vous propose cet article à titre d'inspiration. Le code ci-dessus ne fait pas tout. Par exemple, il ne gère pas les applications multi-modules, ou la définition des ACL est strictement limitée à un fichier INI.

J'espère toutefois avoir réuni en une page tout ce qu'il faut savoir pour gérer correctement les accès sur un site. Si vous avez des questions, n'hésitez pas...

Mis à jour le 4 février 09 pour ajouter la gestion des modules.