基于角色的Rails动态查找器

时间:2009-09-15 13:26:41

标签: ruby-on-rails authorization

我正在寻找构建基于角色/作者的查找器的简洁方法的最佳方法?

在我的模型架构中,user可以具有多个(管理员定义的)角色中的一个,例如管理员,区域经理,销售助理:

示例鉴于具有区域经理角色并加入区域A的用户,我希望能够查询她能看到的其他用户,例如:

regional_manager_for_region_a.users 
  => [...] # Array of users joined to region a

regional_manager_for_region_b.users(:all, conditions => { :active => true })
  => [...] # Array of active users joined to region b

administrator.users
  => [...] # Array of all users in system

谢谢,非常感谢任何帮助!

3 个答案:

答案 0 :(得分:2)

我认为你需要建立一些授权机制。

我知道的最好的宝石是declarative_authorization。我个人在生产环境中使用它,我对此感到满意。关于它也有一个railscast

我们的想法是在一个特定文件(config/authorization_rules.rb)中声明“角色和权限”。你说“经理只能读取与之相关的客户”或“管理员可以读写所有用户”。在你的情况下,它看起来像这样:

authorization do

  role :guest do
    # actions here can be done by everyone, even not logged in people
  end

  role :user do
    includes :guest
    # actions here can be done by logged people
  end

  role :manager do
    includes :user #managers do everything users do, plus:

    has_permission_on :sales_region, :to => :read do
      if_attribute :id => is_in {user.sales_region_ids}
    end

    has_permission_on :users, :to => [:update, :read] do
      if_attribute :id => is {user.user_ids_by_sales_region} #defined on the model
    end
  end

  role :admin do
    includes :user
    has_permission_on [:sales_regions, :users], :to :manage
  end

end

privileges do
  privilege :manage do
    includes :create, :read, :update, :delete
  end
end

指定后,您可以修改模型,以便使用declarative_authorization。另外,让我们定义user_ids_by_sales_region方法

class User < ActiveRecord::Base

  using_access_control # this enables DA

  def users_by_sales_region
    sales_regions.collect{ |sr| sr.users }.flatten.uniq
  end

  def user_ids_by_sales_region
    users_by_sales_region.collect{ |u| u.id }
  end
end

您还必须拥有current_user方法,以及获取当前用户角色的方法。请参阅readme上的“提供插件的要求”部分。

然后你可以用with_permissions_to

做你想做的事
manager = User.find(...)
manager.users.with_permissions_to(:read) # the users from his region
manager.users.with_permissions_to(:read).find(:all, conditions => { :active => true })
manager.users.with_permissions_to(:write) #returns no users, managers can't edit them

admin = User.find(...)
admin.users.with_permissions_to(:write) #will return all users

这意味着一开始就要付出一点努力,但稍后会大大简化应用程序。此外,您还有其他功能,例如根据当前用户的权限隐藏/显示部分视图,以及禁止访问特定控制器操作。

此外,它应该适用于分页等。

还有另一个名为cancan的声明性授权gem。我没有这方面的经验,但如果由Ryan Bates完成,那一定很好(他也有railscast。但是,我不认为它允许模型扩展,这是你现在似乎需要的。

答案 1 :(得分:0)

我的答案对于简单的发现者来说很好;但是,它不是很灵活,与will_paginate插件不兼容。有没有人知道一种更好的方法来清晰地确定用户@current_user能够管理的范围?

由于


刚刚回答了我自己的问题,通过覆盖默认的关联扩展名,如下所示。知道评论或替代方案仍然很棒!

class User < ActiveRecord::Base
  has_many :users do
    def find(*args)
      scope = args.first || :all
      options = args.extract_options!

      return User.find(args.first, options) if proxy_owner.admin?

      users = []
      proxy_owner.sales_regions.collect do |sales_region|
        users += sales_region.users
      end

      users.uniq
    end
  end
end

答案 2 :(得分:0)

为了跟进我对egarcia答案的评论,我最终决定在限制模型上声明named_scopes。例如:

# app/models/account.rb
class Account < ActiveRecord::Base
  named_scope :visible_to, lambda { |user| 
    return {} if user.can_see_all_accounts?
    { :conditions => ['sales_area_id IN (?)', user.sales_area_ids] } 
  }
end

# app/controllers/accounts_controller.rb
class AccountsController < ApplicationController
  def index
    @accounts = Account.visible_to(@current_user)
    ...
  end
end