跳至主要内容

[gem] Rolify

# Gemfile
# Very simple Roles library without any authorization enforcement supporting scope on resource objects (instance or class). Supports ActiveRecord and Mongoid ORMs.
gem 'rolify', '~> 5.2'

設定

$ rails g rolify Role User
$ rails db:migrate

若使用的 id 是 uuid,則在 migrate 檔中記得要作對應的修改:

# db/migrate/20180619081033_rolify_create_roles.rb

class RolifyCreateRoles < ActiveRecord::Migration[5.2]
def change
create_table :roles, id: :uuid do |t|
t.string :name
t.references :resource, :polymorphic => true, type: :uuid

t.timestamps
end

create_table(:users_roles, :id => false) do |t|
t.references :user, type: :uuid
t.references :role, type: :uuid
end

add_index(:roles, [ :name, :resource_type, :resource_id ])
add_index(:users_roles, [ :user_id, :role_id ])
end
end

如果角色需要 scope 在某個 model 底下

假設 Event 底下會有許多不同的 roles:

# app/models/event.rb
class Event < ActiveRecord::Base
resourcify
end

新增刪除角色

新增角色(role)的時候, 角色可以被定義在 Global, Class 或 Instance 這幾個不同層級:

# define GLOBAL role
user.add_role :admin

# define a role scope for specific CLASS resource
user.add_role :admin, Organization

# define a role scope for specific INSTANCE resource
user.add_role :organization_manager, Organization.first

# remove a role 刪除的時候可以不用指定 instance
user.remove_role :moderator

角色查詢

instance method

回傳 true 或 false:

user.has_role? :admin            # 如果當初角色是定義在 Global
user.has_role? :admin, Organization # 如果當初角色是定義在 Class
user.has_role? :admin, Organization.first # 如果當初角色是定義在 Instance
user.has_role? :admin, :any # 不管當初角色定義在哪一個 scope
user.roles_name.include? 'admin' # 不管當初角色定義在哪一個 scope,效能可能較好

# user.has_all_roles? 是否具有所有列出的角色
user.has_all_roles? :admin, {name: :event_member, resource: event}

# user.has_any_role? 是否具有所列的其中一種角色
user.has_any_role? (:admin, {name: :event_member, resource: event})

# user.only_has_role? 是否只具有這個角色
user.only_has_role? :admin

如果當初角色定義在 instance 層級,那麼用 global 的查詢方式回查不到,但用 :any 則可以查到。 如果當初角色定義在 global 層級,那麼用 instance 的方式也查得到。

# instance method for role (user)
user.roles # 列出該使用者所有的 roles,[ActiveRecord, ActiveRecord]
user.roles_name # 以陣列顯示使用者的所有角色名稱,["admin", "event_member"]

# instance method for resource
organization.roles # 列出 organization instance 中所有的 roles
organization.applied_roles # 列出 organization instance 和 Organization Class 的所有角色

class method

##
# Class method for role (user)
##
User.with_role(:admin, :any) # 不論 :admin 在哪一層級
User.with_any_role(:reviewer, :admin) # 列出所有具有 :admin 或 :reviewer 角色的 users
User.with_all_roles(:site_admin, :admin) # 列出同時具有 :site_admin 和 :admin 的 User

##
# Class method for resource
##
# 列出具有 admin 角色的所有 organization instances
Organization.with_role(:admin)

# 列出不具有 admin 角色的所有 organization instances
Organization.without_role(:admin)

# 列出所有具有 admin 角色的 organization instances 而且是屬於 current_user 的
Organization.with_role(:admin, current_user)

# 列出所有具有 admin 或 user 角色的 organization instances,而且是屬於 current_user 的
Organization.with_roles([:admin, :user], current_user)

# 回傳帶有 Role::ActiveRecord 的陣列,[Role::ActiveRecord, Role::ActiveRecord]
# 列出 Organization 中的所有 roles
Organization.find_roles

# 列出 Organization 中具有 :admin 角色的所有 roles
Organization.find_roles(:admin)

# 列出 Organization 中具有 :admin 角色的所有 roles,且是屬於 current_user 的
Organization.find_roles(:admin, current_user)

rolify 提供的 callback

before_add
after_add
before_remove
after_remove