Rails 使用 Pundit 權限管理
使用 pundit gem 當作權限管理的物件,透過一些簡單範例來理解使用方法。
在開發 Rails 專案,我們時常會碰到需要權限控管的時候,如果在 controller 裡面寫一堆 before_action
決定是否權限通過,反而會讓檔案變得肥大,這時候如果用另一個 class 來專門處理權限邏輯的話,那該有多好。pundit 這個 gem 就是專門來處理這件事情的。
範例
當安裝完之後我們會統一將檔案放在 app/policies/
之下,安裝完之後會有兩件事情需要注意:
- pundit 會主動去找 controller 對應的 policy
PostsController -> PostPolicy
- pundit 會要求使用
policy_scope
對撈取資料進行控管,否則會噴錯誤
Pundit::PolicyScopingNotPerformedError
除非使用 skip_after_action
skip_after_action :verify_authorized
skip_after_action :verify_policy_scoped
剛剛提到了會主動去找對應的 policy 檔案,如果想要手動指定的話,也是可以的。
直接在 controller 內呼叫
class FooController
def show
authorize Post # 他會去找 PostPolicy#show
# 帶參數的方式
@post = Post.find(params[:id])
authorize @post, :update?
# 手動指定 policy_class
# 不然他會優先去找參數的 class
# https://github.com/varvet/pundit/blob/main/lib/pundit/policy_finder.rb
authorize @post, policy_class: PublicationPolicy
end
end
以上是關於 controller policy 的部分。接著我們可以來看看針對資料控管範例。
通常會應用在 不同的角色可以看到不同的資料欄位,或是不同撈取資料方式判斷使用者能否通過權限控管。使用方法於原本的 policy class 內新增 Scope
class
class FooPolicy
class Scope
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
if user.admin?
scope.where(receipt_active: true)
else
organization_ids = user.organization_members.admin.pluck(:organization_id)
scope.where(receipt_active: true).where(id: organization_ids)
end
end
end
end
user
參數不代入的話會先去找 current_user
所以在寫法上我們可以忽略這個參數,這個範例就是使用者不同 role 時資料撈法的範例。接著我們只要在 controller 呼叫即可。
def index
@organization_list = policy_scope(Organization, policy_scope_class: ReceiptSystemPolicy::Scope).pluck(:title, :id)
end
以上為平常使用 pundit 當權限控管的範例。