实现多个用户角色

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

但是,我不知道如何继续我正在创建的系统。 我目前正在开发一个用户有很多角色的系统。 因此,拥有一个状态从未经证实确认然后可能是管理员的用户并不简单。

用户现在有许多角色,可以是潜在的骑自行车的coorinatormanger论坛管理员商店管理员超级管理员筹款人

层次结构如下:

超级管理员

论坛管理员商店管理员

骑自行车的人协调员经理筹款人

潜在

但是,一个状态机不会在这里删除它,因为一个用户完全可以同时拥有所有上述角色。

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

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 

而且这只是一点点。 所以我的问题是:

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

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

 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打包?

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

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

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

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

我的应用程序控制器中还有一堆这样的方法, only_superadminsonly_cyclists ,我通过各种子控制器中的before_method方法调用它们。

这个可以吗? 我应该使用cancan还是什么?

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

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

而allowed_roles方法将创建这些方法

 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 

所以这将以编程方式创建这些方法:

 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 

虽然我不明白为什么那不起作用,但这并不会让我觉得太有效率。

也许allowed_roles应该是这样的:

 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 

我只想要一些指针:)

如何创建gem以使allowed_roles方法可用于用户模型可用的控制器和多multiroles

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

恢复答案:

对于处理模型的角色,一个很好的选择是使用gem rolify 。 有了它,您可以轻松定义所需的任意角色,并将您想要的多个角色关联到您的用户。 它使用简单,只需按照此处的官方文档。

CanCan (或其inheritance者CanCanCan )用于处理权限。 您将在文件app/models/ability.rb定义具有每个角色的用户(使用rolify定义)的权限。 然后,在控制器或视图中,您只需validation用户是否有权对资源执行操作。 例如,在您的控制器中validation授权,如@comment = Comment.new(params); authorize! :create, @comment @comment = Comment.new(params); authorize! :create, @comment @comment = Comment.new(params); authorize! :create, @comment ,并在您的视图中validation授权, if can? :create, Comment if can? :create, Comment 。 请参阅此处的官方文档,了解如何设置和使用CanCan。

根据您的具体问题:

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

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

resourcify添加到用户将访问的任何类。 例如:

 class Forum < ActiveRecord::Base resourcify end 

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

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

您甚至可以将角色范围限定为一个资源或实例:

 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类:

 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 

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

 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! 方法。 例如:

 # 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,并在每个操作之前自动加载和授权(或不授权)资源:

 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的入门知识。

我希望这有助于指导您解决问题。