实现多个用户角色 [英] Implementing multiple user roles

查看:40
本文介绍了实现多个用户角色的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 state_machine 取得了巨大的成功,并且喜欢它通过几行代码动态创建的类方法.

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 with the system I'm creating. I'm currently developing a system where users have many roles. So it's not a simple as having a user whose state goes from unconfirmed to confirmed and then potentially to admin.

一个用户现在有很多角色,可以是潜力自行车手协调员经理strong>、论坛管理员商店管理员超级管理员筹款人.

A user now has many roles, and can be a potential, a cyclist, a coorinator, a manger, a forum admin, a store admin, a super admin and a fundraiser.

所以层次结构是这样的:

So the hierarchy goes like this:

超级管理员

论坛管理员商店管理员

自行车手协调员经理筹款人

潜力

然而,一台状态机不会在这里截断它,因为一个用户完全有可能同时拥有上述所有角色.

However, one state machine won't cut it here, because it's completely possible that one user can have all of the above roles at the same time.

我正在实现我自己的类方法,以在某种程度上模拟状态机:

I'm implementing my own class methods like this to sort of emulate state-machine somewhat:

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

而且这只是一点点废话.所以我的问题是:

And it's just a bit of a faff. So my questions are:

1) 我做得对吗?您将如何处理具有多个角色的用户?

1) Am I doing it right? How would you handle users with multiple roles?

2) 即使我做得对,我也想创建一个 state_machine-esque DSL,所以当我需要创建一个新角色时,比如说runner",我可以在我的模型中做这样的事情:

2) Even if I am doing it right, I'd like to create a state_machine-esque DSL, so when I need to create a new role, let's say 'runner', I can just do something like this in my model:

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

我应该如何创建多角色方法?在 lib 里面?准备好打包作为我的第一个 Gem 了吗?

How should I create that multiroles method? Inside lib? ready to be packaged off as my first Gem?

我不知道如何动态创建方法,但我想开始:)

I have no idea how to dynamically create methods, but I'd like to start :)

想一想,也许multiroles方法可以通过Roles.all动态获取所有角色,并自动添加上述方法!甚至可以处理 has_many :jobs has_many :roles, through: :jobs

Just a thought, maybe the multiroles method could dynamically get all the roles via Roles.all and automatically add the above methods! Maybe even take care of the has_many :jobs has_many :roles, through: :jobs

另外,我应该如何验证这些角色?我目前正在控制器的 before 块中执行此操作:

Also, how should I be authenticating these roles? I'm currently doing this in a before block in my controllers:

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

我的应用程序控制器中也有一堆这些方法,only_superadminsonly_cyclists 等,我通过 before_method 方法调用它们各种子控制器.

I also have a bunch of these methods in my application controller, only_superadmins, only_cyclists ect and I call them via the before_method method in various sub-controllers.

这样好吗?我应该使用cancan还是什么?

Is this okay? Should I be using cancan or something?

如果我做得对,我想知道我应该如何使用我的 Gem 动态创建这些方法.我正在考虑以下几点:

If I am doing it right, I'm wondering how I should dynamically create these methods with my Gem. I'm thinking something along these lines:

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

并且 allowed_roles 方法会创建这些方法

and the allowed_roles method would create these methods

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

这样就可以以编程方式创建这些方法:

So that would programatically create these methods:

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

虽然我不明白为什么这行不通,但这并没有让我觉得效率太高.

While I don't see why that wouldn't work, that doesn't strike me as too efficient.

也许 allowed_roles 应该是这样的:

Maybe allowed_roles should look like this:

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

我真的只是想要一些指针:)

I just want some pointers really :)

如何创建 gem 以使 allowed_roles 方法可用于控制器,而 multiroles 可用于用户模型?

How do I create a gem to make the allowed_roles method available to the controllers and multiroles available to the user model?

cancan 可以这样管理多个角色吗?我应该只使用它吗?

Can cancan manage multiple roles like this? Should I just be using that?

推荐答案

恢复答案:

为了处理模型的角色,一个不错的选择是使用 gem rolify.有了它,您可以轻松定义任意数量的角色,并将任意数量的角色关联到您的用户.使用起来很简单,只需按照官方文档这里.

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.

Can(或其继承者CanCanCan) 用于处理权限.您将在文件 app/models/ability.rb 中定义具有每个角色(使用 rolify 定义)的用户有权执行的操作.然后,在控制器或视图中,您只需验证用户是否有权对资源执行操作.例如,在您的控制器中,您验证授权,如 @comment = Comment.new(params);授权!:create, @comment,在您看来,您可以像 一样验证授权,如果可以?:创建,评论.请参阅官方文档此处了解如何设置和使用 CanCan.

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.

将这些应用于您的具体问题:

将 Rolify (gem "rolify") 和 CanCan (gem "cancan") gem 添加到您的 Gemfile.

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

执行 rails shell 命令 rails g rolify Role User 创建一个名为 Role 的新类(或使用您喜欢的名称)并在您现有的类 User 中添加一些类方法.由于新类 Role 将向您的数据库添加一个表 Role,您必须运行 rake db:migrate(使用 ActiveRecord 时).

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).

resourcesify 添加到用户将访问的任何类.例如:

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

class Forum < ActiveRecord::Base
  resourcify
end

完成这些步骤后,您的 User 类将配备 add_roleremove_rolehas_role 方法,您可以使用它们添加任意数量的角色:

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

所以你可以这样写你的 User 类:

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

要验证这些角色,您可以使用 CanCan.执行 rails shell 命令 rails g cancan:ability 以生成文件 app/models/ability.rb,您将在其中定义角色的权限.

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

在您的控制器中,您可以调用 authorize! 方法.例如:

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

或者您可以在控制器开始时包含 faloowing,并且在每个操作之前自动加载和授权(或不)资源:

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

RailsCast 上观看本教程,了解开始使用 CanCan.

Watch this tutorial at RailsCast for getting started with CanCan.

我希望这能帮助您解决问题.

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

这篇关于实现多个用户角色的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆