Implementing multiple user roles

后端 未结 1 1801
情深已故
情深已故 2021-01-31 21:53

I\'ve had great success using the state_machine and love the class methods it dynamically creates through just few lines of code.

However, I\'m not sure how to proceed

1条回答
  •  醉酒成梦
    2021-01-31 22:17

    Resumed answer:

    For handling roles for your model, a good options is to use the gem rolify. With it you can easily define as many roles you want and associate as many roles as you want to your User. It is simple to use, just follow the official documentation here.

    CanCan (or its sucessor CanCanCan) is used to handle permissions. You will define what an User with each role (defined with rolify) is authorized to do in the file app/models/ability.rb. Then, in controllers or views, you just verify if user is authorized to perform an action for a resource. For example, in your controller you verify the authorization like @comment = Comment.new(params); authorize! :create, @comment, and in your view you verify the authorization like if can? :create, Comment. Refeer to the official documentation here for learning how to setup and use CanCan.

    Aplying these to your specific problem:

    Add Rolify (gem "rolify") and CanCan (gem "cancan") gems to your Gemfile.

    Execute the rails shell command rails g rolify Role User to create a new class named Role (or use the name you prefer) and add some class methods in your existing class User. As the new class Role will add a table Role to your database, you have to run rake db:migrate (when using ActiveRecord).

    Add resourcify to any class that will be accessed by the User. For example:

    class Forum < ActiveRecord::Base
      resourcify
    end
    

    Once you have done these steps, your User class will be equiped with the methods add_role, remove_role and has_role and you can use them to add as many roles as you wish:

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

    And you can even scope a role to one resource or instance:

    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 you could write your User class like this:

    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
    

    To authenticate those roles you may use CanCan. Execute the rails shell command rails g cancan:ability to generate the file app/models/ability.rb where you will define the permissions for your roles.

    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 your controllers you may call the authorize! method. For example:

    # 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
    

    Or you could include the faloowing at the beggining of your controller and the resource is automatically loaded and authorized (or not) before each action:

    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
    

    Watch this tutorial at RailsCast for getting started with CanCan.

    I hope this can help to guide you trough your problem.

    0 讨论(0)
提交回复
热议问题