Pour compenser le fait qu’il y ait eu deux articles la semaine passée, celui de cette semaine sera light ;)
Il existe plusieurs systèmes d’identifications pour les applications Rails. Mes deux préférés sont authlogic et devise. Mais clearance est pas mal non plus.

Je vais parler du premier. Lorsque vous avez implémenté votre système d’identification avec ce gem, vous vous rendrez rapidement compte qu’un même utilisateur peut être connecté plusieurs fois depuis des machines ou des navigateurs différents.
Ce base, cela peut être intéressant afin d’éviter d’avoir à redemander à vos utilisateurs de se reconnecter trop régulièrement.

Mais dans d’autres cas (ou vous désirez éviter le partage de comptes par exemple), ce n’est pas l’effet désiré.
Après avoir cherche un petit peu, j’ai posté la solution à ceci sur stackoverflow.

Dans votre modèle de session (par défaut UserSession), ajoutez le code suivant :

before_destroy :reset_persistence_token
before_create  :reset_persistence_token

def reset_persistence_token
    record.reset_persistence_token
end

Nous créons deux callbacks.
Ainsi, notre méthode reset_persistence_token sera exécutée à chaque fois que nous créons ou supprimons une session utilisateur (à chaque fois que l’utilisateur se connecte ou se déconnecte).

Cette méthode réinitialise un token qui est également situé dans la session de l’utilisateur et qui nous permet de l’identifier.
Nous sommes obligés de la définir car elle l’est à l’origine, uniquement dans le modèle User. C’est dans la session que nous désirons réinitialiser ce token.
D’ou le record.. record retourne l’utilisateur auquel nous réinitialisons le token.

Ainsi, à chaque fois que notre utilisateur se connectera ou se déconnectera de l’application, le token sera modifié.
Et nous n’aurons donc pas la possibilité d’avoir deux machines connectées avec la même session :)

Avec rails comme avec tout framework, vous allez rapidement avoir besoin de gérer l’identification des utilisateurs et leurs autorisations.
Pour leur identification, il existe divers plugins. Notamment Restful Authentication, AuthLogic et (mon préféré) Clearance.
Pour la gestion des autorisations, je vais vous parler de ACL9.

L’idée est simple. Après avoir identifié votre utilisateur, vous avez accès à un objet current_user, qui contient l’objet relatif à l’utilisateur actif (généralement une instance du model User. Mais cela peut être n’importe quoi).
ACL9 va ajouter deux tables :
- Role, qui contiendra tous les rôles utilisateurs existants. Un peu comme des groupes. Chaque rôle pouvant être associé à une instance d’un objet.
Vous pourrez ainsi avoir un rôle « admin », global, qui permettra de définir les accès de l’administrateur.
Et un rôle « éditeur », relatif uniquement à l’objet ayant pour id « 1″ du modèle « News ».

- UserRole, qui fera la relation entre un rôle et un utilisateur (has_and_belongs_to_many).

Voici les migrations nécessaires pour ces deux tables :

create_table :roles, :force => true do |t|
    t.column   :name,                            :string,                    :limit => 40
    t.column      :authorizable_type",      :string,                    :limit => 40
    t.column      :authorizable_id,           :integer
    t.timestamps
end
create_table :roles_users", :id => false, :force => true do |t|
    t.column :user_id,                          :integer
    t.integer  :role_id,                           :integer
    t.timestamps
end

Vous devez bien évidemment créer un modèle « Role », qui représentera cette table. Il est inutile de créer un modèle RolesUser. Cela sera géré automatiquement par Rails.

class Role < ActiveRecord::Base
    acts_as_authorization_role
end

Vous noterez l’ajout de « acts_as_authorization_role », qui va implémenter les méthodes de rôles relatives à ACL9.
Dans notre table user, nous allons également devoir ajouter un appel à une méthode spécifique de ACL9 afin d’ajouter les méthodes relatives à l’utilisateur.

class User < ActiveRecord::Base
    acts_as_authorization_subject
end

Et dans tous les modèles ou nous désirons avoir une gestion des droits d’accès, nous allons ajouter :

class Foo < ActiveRecord::Base
    acts_as_authorization_object
end

ACL9 vous a maintenant ajouté diveres méthodes dans vos objets :
Dans votre modèle utilisateur :

  • has_role?(role, object = nil) – Retournera « true » si l’utilisateur a le rôle demandé en global ou sur l’objet précisé.
  • has_role!(role, object = nil) – Donnera le rôle nommé à l’utilisateur
  • has_no_role!(role, object = nil) – Supprimera le rôle nommé pour l’utilisateur
  • has_roles_for?(object) – Retourne true si l’utilisateur a un quelconque rôle sur l’objet
  • has_role_for?(object) – Alias de has_roles_for?
  • roles_for(object) – Retourne tous les rôles de l’utilisateur pour cet objet
  • has_no_roles_for!(object) – Supprime tous les rôles de l’utilisateur pour l’objet
  • has_no_roles! – Supprime tous les rôles de l’utilisateur

Et dans chacun de vos modèles objets (Foo plus haut) :

  • accepts_role?(role, subject) – Retourne true si subject a accès au rôle courant
  • accepts_role!(role, subject) – Donne accès à subject au rôle courant
  • accepts_no_role!(role, subject) – Supprime l’accès utilisateur au rôle
  • accepts_roles_by?(subject) – Retourne true si l’utilisateur a un rôle quelconque sur l’objet
  • accepts_role_by?(subject) – Alias de accepts_roles_by?
  • accepted_roles_by(subject) – Retourne tous les utilisateurs ayant un rôle pour cet objet

Maintenant que nous avons implémenté nos autorisations et que nous avons vu les diverses méthodes pour les utiliser, limitons les accès à nos actions !

class MyOwnController < ApplicationController
    access_control do
        # Autorise tous les utilisateurs ayant le rôle "superadmin" à accéder à l'objet
        allow :superadmin

        # Autorise tous les utilisateurs ayant le rôle "creator" sur @news
        allow :creator, :at => :news

        # Autorise les utilisateurs anonymes et ceux qui sont enregistrés sur la page d'accueil
        # Et refuse les utilisateurs ayant le rôle "banned"
        action :index do
            allow anonymous, logged_in
            deny :banned
        end

        # Autorise les utilisateurs ayant le rôle "manager" sur l'objet @news. Sauf pour l'action "destroy"
        allow :manager, :at => :news, :except => [:destroy]
    end

    def index
        @news = News.find_by_id params[:id]
        # Le code relatif à l'action "index"
    end
end

Du coup on peut maintenant gérer de manière particulièrement avancée les rôles dans notre application.

 
Fork me on GitHub