can_plan集成了cancancan和consul这两个gem的功能,使用简单的DSL描述某个角色对单个资源或某类资源的操作权限,可以完整控制用户对资源的访问权限。如要使用它,系统必须有用户模型和角色模型。用户可以有多个角色,然后在角色上设置权限,让用户,根据角色不同获取不同权限,以下示例都会基于用户和角色的模型。

安装方式

  在gemfile中加入can_play。

gem 'can_play'

  运行bundle,安装完成后执行如下命令

rails generate can_play:install

  执行该命令会在initializer和locales文件夹下生成配置文件。 initializer文件夹下的can_play.rb是can_play的基本配置文件。 locales下的can_play.zh-Cn.yml文件用于描述权限中文名称。

# path_to_config/initializers/can_play.rb
CanPlay::Config.setup do |config|
  config.user_class_name = 'User'
  config.role_class_name = 'Role'
  config.role_resources_relation_name = 'role_resources'
  config.super_roles = ['超级管理员']
  self.role_judge_method = 'role_is?'
end

  can_play.rb配置文件中,可以传入用户类名(默认User)、角色类名(默认Role)、以及角色和权限的关联的名称(默认role_resources),role_resources_relation_name是角色类和操作权限资源之间的关联,以及判断当前用户角色的方法(默认role_is?)。

# path_to_config/config/locales/can_play.zh-Cn.yml
---
zh-CN:
  can_play:
    class_name:
      test: 测试
      contract: 合同
    authority_name:
      common:
        list: 列表查看
        create: 新建
        read: 查看
        update: 修改
        delete: 删除
        crud: 管理
        menus_roles: 菜单分配
        role_resources: 权限分配
        improve: 完善信息
      contract:
        terminate: 终止
        purchaser_confirm: 采购人确认
        supplier_confirm: 供应商确认

  can_play.zh-CN.yml是中文翻译文件,在这里写下权限的英文和中文名称的对应,在前端就可以获取到权限的中文描述,其中common是一些常用权限名称,特别的权限名称,可以单独写,如contract下的terminate权限是合同独有的权限名称,必须单独写,而class_name也可以写上资源名称,如contract,如果不写,会默认去ActiveRecord的翻译文件下去取中文翻译。

DSL文件描述权限的方法

  dsl文件写法如下:

# 类名,或文件名叫什么并不要紧,关键是要'include CanPlay'
class Resource
  include CanPlay
  self.module_name = '核心模块'

  # 所有limit块、collection块和member块中都注入了user这个变量,指向当前登录用户,可直接使用。

  group Contract do |klass|

    # 描述某个用户可以查看到哪些合同条目。
    limit do
      if user.is_admin?
        klass.all
      elsif user.role? '供应商'
        klass.where(supplier: user.supplier)
      elsif user.role? '采购人'
        klass.where(purchaser: user.purchaser)
      else
        klass.none
      end
    end

    # 描述某个用户可以是否而已查看合同列表、创建合同。
    collection [:list, :create], klass do
      user.is_admin?
    end

    # 描述某个用户可以是否可以查看、更新某个合同。
    member [:read, :update], klass do |obj|
      if user.is_admin?
        true
      elsif user.role? '供应商'
        obj.supplier.is? user.supplier
      elsif user.role? '采购人'
        obj.purchaser.is? user.purchaser
      else
        false
      end
    end

    # 描述某个用户可以是否可以删除、终止某个合同。
    member [:delete, :terminate], klass do |obj|
      user.is_admin?
    end
  end

end

  group是一个用于对资源做分组的宏。group方法可以只接一个类或模型,在其后再接一个代码块,并把刚才传给group的类或模型,传给这个代码块。他是limit、collection、member方法的容器。

  limit方法用于控制某个用户可以查看的资源的额列表,如Contract类下的limit限制了管理员可以查看所有合同,供应商和采购人只能查看和自己相关的合同。limit方法会让在controller中生成一个动态方法,current_power.contracts,这个方法返回的是是我们再limit中写如的对象,这样就能根据用户的信息返回不同的资源数组。

  collection方法,可以控制某个用户对某类资源的控制权限。如list和create权限,在controller中,我们可以用authorize!(:read, Contract)来限制用户的访问。

  member方法,可以控制用户对某个资源的控制权限,如read权限,在controller中我们可以用authorize!(:read, @contract)来限制用户的访问。

  self.module_name = '核心模块'是用来处理在多模块开发的环境下,各个模块可能有自己的resource文件,并可能出现中文的重名,权限最终要集中管理,module_name可以做个简单的分隔,让用户清楚某个权限属于哪个模块。

如何使用

  对使用有不清楚的,可以查看https://github.com/happyming9527/can_play/wiki