2014-05-10 11 views
12

Ich hatte großen Erfolg mit der state_machine und liebe die Klassenmethoden, die es dynamisch durch nur ein paar Zeilen Code erstellt.Implementieren mehrerer Benutzerrollen

Allerdings bin ich mir nicht sicher, wie ich mit dem System fortfahren soll, das ich erstelle. Ich entwickle gerade ein System, in dem Benutzer viele Rollen haben. Es ist also nicht einfach, einen Benutzer zu haben, dessen Status von unbestätigt bis und dann möglicherweise admin geht.

Ein Benutzer hat jetzt viele Rollen und kann ein Potential, ein Radfahrer, ein coorinator, ein Krippe, ein Forum Admin, ein Shop Admin, ein Super-Administrator sein und eine Spendenaktion.

So geht die Hierarchie wie folgt aus:

superadmin

Forum Admin, store Admin

Radfahrer, Koordinator, Krippe, Fundraiser

Potential

Allerdings wird eine Zustandsmaschine es hier nicht geschnitten, weil es durchaus möglich ist, dass ein Benutzer alle oben genannten Rollen in der gleichen Zeit haben kann.

ich meine eigene Klasse Methoden wie diese Umsetzung Art etwas state-Maschine zu emulieren:

class User < ActiveModel::Base 
    has_many :jobs 
    has_many :roles, through: :jobs 

    def role_array 
     self.roles.pluck(:role) 
    end 

    def has_role?(role) 
     role_array.include?(role) 
    end 

    # checking 
    def is_superadmin? 
     role_array.include?('superadmin') 
    end 


    # changing 
    def add_role(role) 
     self.update_attributes(accepted_at: Time.now) if self.is_only_potential? 
     self.user_roles.create(role_id: Role.find_by(role: role).id) if !self.has_role?(role) 
    end 

    def remove_role(role) 
     self.user_roles.find_by(role_id: Role.find_by(role: role).id).destroy if self.has_role?(role) 
    end 

    def make_superadmin! 
     add_role('superadmin') 
    end 

    def denounce_superadmin! 
     remove_role('superadmin') 
    end 

end 

Und es ist nur ein bisschen ein faff. Also meine Fragen sind:

1) mache ich es richtig? Wie würden Sie mit Benutzern mit mehreren Rollen umgehen?

2) Auch wenn ich es richtig mache, würde Ich mag ein state_machine-esque DSL zu schaffen, so dass, wenn ich brauche eine neue Rolle zu erstellen, sagen wir mal ‚Läufer‘, kann ich nur so etwas tun Dies in meinem Modell:

class User < ActiveModel::Base 
    has_many :jobs 
    has_many :roles, through: :jobs 

    multiroles initial: :potential do 
     roles [:superadmin, :forum_admin, :store_admin, :cyclist, :coordinator, :manager, :fundraiser, :potential] 
     # dynamically creates the above methods for getting and setting for all roles 
    end 

Wie soll ich diese multirole Methode erstellen? Inside lib? bereit, als mein erster Edelstein abgepackt zu werden?

Ich habe keine Ahnung, wie dynamisch Methoden zu schaffen, aber ich mag :)

nur einen Gedanken starten, vielleicht die multiroles Methode dynamisch alle Rollen über Roles.all bekommen konnte und automatisch die oben genannten Methoden hinzufügen! Vielleicht kümmern Sie sich auch um die has_many :jobshas_many :roles, through: :jobs

Auch, wie sollte ich diese Rollen authentifizieren? Ich tue dies derzeit in einem vor Block in meinem Controller:

def only_superadmins 
    redirect_to root_url if !current_user.has_role?('superadmin') 
end 

ich auch ein paar dieser Methoden in meiner Anwendung Controller verfügen, only_superadmins, only_cyclists ect und ich nenne sie über die before_method Verfahren in verschiedenen Teil Controller.

Ist das okay? Sollte ich Cancan oder etwas verwenden?

Wenn ich es richtig mache, frage ich mich, wie ich diese Methoden dynamisch mit meinem Edelstein erstellen soll. Ich denke, etwas in dieser Richtung:

class panel_controller < ApplicationController 
    allowed_roles [:super_admin, :forum_admin, :store_admin] 
end 

und die allowed_roles Methode, um diese Methoden So erstellen würde

def allowed_roles(role_array) 
    role_array.each do |role| 
     define "only_#{role.to_s}s" do |arg| 
      redirect_to root_url if !current_user.has_role?(arg.to_s) 
     end 
    end 
end 

das würde diese Methoden programmatisch erstellen:

def only_super_admins 
    redirect_to root_url if !current_user.has_role?('super_admin') 
end 

def only_forum_admins 
    redirect_to root_url if !current_user.has_role?('forum_admin') 
end 


def only_store_admins 
    redirect_to root_url if !current_user.has_role?('store_admin') 
end 

Während ich don‘ Warum das nicht funktionieren würde, finde ich nicht so effizient.

Vielleicht allowed_roles sollte wie folgt aussehen:

def allowed_roles(wanted_roles) 
    redirect_to root_url unless (current_user.role_array & wanted_roles).empty? # it's ONLY empty when any of the current_user roles exists in the wanted_roles array 
end 

Ich möchte nur einige Hinweise wirklich :)

Wie kann ich ein Juwel erstellen die allowed_roles Methode zur Verfügung, an die Controller zu machen und multiroles zur Verfügung das Benutzermodell?

Kann cancan mehrere Rollen wie das verwalten? Soll ich das nur benutzen?

Antwort

16

beantworten Resumed:

Für Rollen für Ihr Modell, eine gute Optionen Handhabung ist die Perle rolify zu verwenden. Damit können Sie beliebig viele Rollen definieren und Ihrem Benutzer beliebig viele Rollen zuordnen. Es ist einfach zu bedienen, folgen Sie einfach der offiziellen Dokumentation here.

CanCan (oder dessen sucessor CanCanCan) verwendet Berechtigungen zu handhaben. Sie definieren, was ein Benutzer mit jeder Rolle (definiert mit rolify) in der Datei app/models/ability.rb tun darf. In Controllern oder Ansichten überprüfen Sie dann, ob der Benutzer berechtigt ist, eine Aktion für eine Ressource auszuführen. In Ihrem Controller überprüfen Sie beispielsweise die Autorisierung wie @comment = Comment.new(params); authorize! :create, @comment, und in Ihrer Sicht überprüfen Sie die Autorisierung wie if can? :create, Comment.Beziehen Sie sich auf die offizielle Dokumentation here, um zu erfahren, wie Sie CanCan einrichten und verwenden.

diese spezifische Problem aplying:

hinzufügen Rolify (gem "rolify") und CanCan (gem "cancan") Edelsteine, um Ihre Gemfile.

Führen Sie den Befehl rails shell rails g rolify Role User aus, um eine neue Klasse mit dem Namen Role zu erstellen (oder verwenden Sie den von Ihnen bevorzugten Namen) und fügen Sie Klassenmethoden in Ihre vorhandene Klasse User ein. Wenn die neue Klassenrolle eine Tabellenrolle zu Ihrer Datenbank hinzufügt, müssen Sie rake db:migrate ausführen (bei Verwendung von ActiveRecord).

Fügen Sie resourcify zu jeder Klasse hinzu, auf die der Benutzer zugreifen wird. Zum Beispiel:

class Forum < ActiveRecord::Base 
    resourcify 
end 

Sobald Sie diese Schritte durchgeführt haben, wird Ihre Benutzerklasse mit den Methoden add_role, remove_role und has_role und Sie können sie ausgerüstet werden verwenden, wie viele Rollen hinzufügen, wie Sie wollen:

user.add_role :superadmin 
user.add_role :fundraiser 

user.has_role? :superadmin 
# >> true 
user.has_role? :fundraiser 
# >> true 

und Sie können sogar Umfang eine Rolle auf eine Ressource oder Instanz:

user.add_role :cyclist, Forum 
user.add_role :coordinator, Forum.first 

user.has_role? :cyclist, Forum 
# >> true 
user.has_role? :cyclist, Store 
# >> false 
user.has_role? :coordinator, Forum.first 
# >> true 
user.has_role? :coordinator, Forum.second 
# >> false 

So könnten Sie Ihre User-Klasse wie folgt schreiben:

class User < ActiveModel::Base 
    rolify 
    has_many :jobs 

    # checking 
    def is_superadmin? 
     self.has_role?('superadmin') 
    end 

    # changing 
    def add_new_role(role) 
    self.update_attributes(accepted_at: Time.now) if self.is_only_potential? 
    self.add_role(role) 
    end 

    def make_superadmin! 
     add_new_role('superadmin') 
    end 

    def denounce_superadmin! 
     remove_role('superadmin') 
    end 
end 

Um diese Rollen zu authentifizieren, können Sie CanCan verwenden. Führen Sie den Befehl rails shell rails g cancan:ability aus, um die Datei app/models/ability.rb zu generieren, in der Sie die Berechtigungen für Ihre Rollen definieren.

class Ability 
    include CanCan::Ability 

    def initialize(user) 
    user ||= User.new # guest user 

    if user.has_role? :superadmin 
     can :manage, All # can manage (Read, Create, Update, Destroy, ...) everything 
    elsif user.has_role? :forum_admin 
     can :manage, Forum # can manage (Read, Create, Update, Destroy, ...) any Forum 
    elsif user.has_role? :store_admin 
     can :manage, Store do |store| # Can manage only its own store 
     store.try(:user) == user 
     end 
    elsif user.has_role? :forum_member 
     can :create, Post do |post| 
     if post.forum.members.include? user 
      true 
     end 
     end 
     can :destroy, Post do |post| 
     post.try(:user) == user 
     end 
     can :update, Post do |post| 
     post.try(:user) == user 
     end 
    elsif ... 

    else # Users without role 
     can :read, All 
    end 
    end 
end 

In Ihrem Controller können Sie die authorize! Methode aufrufen. Zum Beispiel:

class PostController < ApplicationController 
    load_and_authorize_resource :post 

    ... 

def create 
    authorize! :create, @post 
    if @post.save 
    redirect_to @post 
    else 
    render :action => 'new' 
    end 
end 

beobachten dieses Tutorial auf :

# app/controllers/posts_controller.rb 
def create 
    @post = Post.new(params[:post]) 
    @post.user = current_user 
    authorize! :create, @post 
    if @post.save 
    redirect_to @post 
    else 
    render :action => 'new' 
    end 
end 

Oder Sie die faloowing sich am Anfang der Steuerung und die Ressource wird automatisch geladen und autorisiert (oder nicht) vor jeder Aktion enthalten könnte RailsCast für den Einstieg in CanCan.

Ich hoffe, dass dies helfen kann, Sie durch Ihr Problem zu führen.

+0

Vielleicht CanCanCan https://github.com/cancancommunity/cancancan anstelle von CanCan verwenden. – Bjoernsen

Verwandte Themen