Tutoriel: Créer une gestion des droits simplifiée pour Rails

Aujourd’hui, nous allons regarder comment mettre en place une gestion des droits simple dans Rails avec Devise.

# Introduction

Vous avez un site à faire où les utilisateurs n’ont pas tous les mêmes droits.

Certains utilisateurs ne peuvent que modifier leurs paramètres à eux, alors que les auteurs peuvent écrire des articles. Les modérateurs, eux, peuvent éditer et supprimer des commentaires, quel que soit l’article sur lequel ils ont été posés. Et bien sur, l’admin, lui, a tout les droits.

Regardons maintenant comment mettre ça en place de manière assez simple.

Prérequis: 

*  Rails
*  Devise (simplement remplaçable, en réalité)

Objectifs: 

*  Pouvoir définir des rôles pour les users.
*  Pouvoir tester simplement si un user appartient à tel ou tel rôle.
*  Pouvoir simplement autoriser ou interdire l’accès à une page/tout un controller à certains rôles.

Postulat: 

*  Votre classe d’utilisateurs est User. 
*  Votre partie admin est dans le module Admin, et tout ses controllers héritent de Admin::ApplicationController. 
*  Votre partie admin est protégée dans son intégralité par Devise.

# Mise en œuvre

Tout d’abord, on vas créer une migration, pour modifier notre table. Nous allons ajouter le champ role_id, qui est un integer, limit => 4, default => 0. Nous l’ajoutons aussi en index, ça peut servir si nous avons besoins de scopes.
Hop, on migre et on s’attaque au model.

Dans le model User, nous allons tout d’abord ajouter notre liste de rôles.
Elle pourrait être séparée, sous forme de classes/yml/autre, mais le sujet de l’article n’est pas là.

Ajoutons donc dans notre classe :

@@roles = [:undefined, :admin, :writer, :moderator, :user]
#pour pouvoir y accéder depuis ailleurs, sans avoir à se répéter def self.roles
     @@roles
end
Maintenant, il nous faut un moyen de changer le rôle d’un user.
attr_accessible :role def role
@@roles[self.role_id || 0] end def role(value)
if role_id = @@roles.index(value)
self.role_id = role_id
     end
end

Il nous faut aussi le moyen de tester quel est le rôle d’un user.

def is?(roles)
     if roles.class.public_method_defined? :'include?'
         role.include?
self.role
     else
         role == self.role
     end
end

Voilà, maintenant, nous pouvons simplement tester si l’utilisateur courrant est l’admin, avec current_user.is? :admin, ou bien lui donner des tableaux : current_user.is? [:admin, :writer]. Mais il nous faudrait aussi pouvoir bloquer ou autoriser certains controllers en fonction de son rôle. Pour ça, nous allons utiliser before_filter.

classe Admin::Application < ApplicationController
    before_filter :authenticate_user!
    before_filter :check_authorisation
    def check_authorization
        @@only ||= User.roles
        @@except ||= []
        authorized_roles = @@only - @@except
        unless current_user.is? authorized_roles
            render :file => "#{Rails.root.to_s}/public/401.html", :status => :unauthorized 
        end
    end
end

Voilà, tout est en place. Pour définir les utilisateurs autorisés à accéder à une page, il suffit de mettre par exemple @@only = [:admin, :moderator], ou bien @@except = [:undefined, :user], dans le controlleur en question.

Quoi, c’est un peu lourd, comme syntaxe ? Et bien simplifions ! Il suffit d’ajouter ces deux methods dans Admin::Aplication

def self.only(*args)
 @@only = *args
end
def self.except(*args)
  @@except = *args
end

Maintenant, nous pouvons faire :

module Admin
    class ArticlesControllers < ApplicationControllers
     only :admin, :writer
    end
end

Conclusion

Cette gestion de permissions n’est pas encore très bonne. Il faudrait lui ajouter la possibilité d’affiner les permissions en fonction des pages, extraire le code dans des modules, et d’autres choses encore. Mais bon, ce n’est pas grave, son but est avant tout pédagogique. Si vous avez besoin d’une bonne gestion de permissions, je vous recommande la gem CanCan, qui est vraiment très bien pour ça.