Pundit มีชุดตัวช่วยที่จะแนะนำคุณในการใช้ประโยชน์จากคลาส Ruby ปกติและรูปแบบการออกแบบเชิงวัตถุ เพื่อสร้างระบบการอนุญาตที่ตรงไปตรงมา แข็งแกร่ง และปรับขนาดได้
สนับสนุนโดย: Varvet
โปรดทราบ ว่า README บน GitHub นั้นแม่นยำด้วย โค้ดล่าสุดบน GitHub คุณน่าจะใช้ Pundit เวอร์ชันที่วางจำหน่าย ดังนั้น โปรดดูเอกสารประกอบสำหรับ Pundit เวอร์ชันที่วางจำหน่ายล่าสุด
bundle add pundit
รวม Pundit::Authorization
ในตัวควบคุมแอปพลิเคชันของคุณ:
class ApplicationController < ActionController :: Base
include Pundit :: Authorization
end
หรือคุณสามารถเรียกใช้ตัวสร้างซึ่งจะตั้งค่านโยบายแอปพลิเคชันพร้อมค่าเริ่มต้นที่เป็นประโยชน์สำหรับคุณ:
rails g pundit:install
หลังจากสร้างนโยบายแอปพลิเคชันของคุณแล้ว ให้รีสตาร์ทเซิร์ฟเวอร์ Rails เพื่อให้ Rails สามารถรับคลาสใดก็ได้ในไดเรกทอรี app/policies/
ใหม่
บัณฑิตมุ่งเน้นไปที่แนวคิดเรื่องชั้นเรียนนโยบาย เราขอแนะนำให้คุณใส่คลาสเหล่านี้ใน app/policies
นี่คือตัวอย่างที่ช่วยให้อัปเดตโพสต์ได้หากผู้ใช้เป็นผู้ดูแลระบบ หรือหากโพสต์ไม่ได้เผยแพร่:
class PostPolicy
attr_reader :user , :post
def initialize ( user , post )
@user = user
@post = post
end
def update?
user . admin? || ! post . published?
end
end
อย่างที่คุณเห็น นี่คือคลาส Ruby ธรรมดา บัณฑิตตั้งสมมติฐานเกี่ยวกับชั้นเรียนนี้ดังต่อไปนี้:
current_user
เพื่อดึงสิ่งที่จะส่งเข้าสู่อาร์กิวเมนต์นี้update?
- โดยปกติแล้ว สิ่งนี้จะแมปกับชื่อของการดำเนินการของคอนโทรลเลอร์โดยเฉพาะแค่นั้นแหละจริงๆ
โดยปกติแล้ว คุณจะต้องการสืบทอดจากนโยบายแอปพลิเคชันที่สร้างโดยตัวสร้าง หรือตั้งค่าคลาสพื้นฐานของคุณเองเพื่อสืบทอดจาก:
class PostPolicy < ApplicationPolicy
def update?
user . admin? or not record . published?
end
end
ใน ApplicationPolicy
ที่สร้างขึ้น วัตถุโมเดลเรียกว่า record
สมมติว่าคุณมีคลาส Post
ตอนนี้ Pundit ให้คุณทำสิ่งนี้ได้ในคอนโทรลเลอร์ของคุณ:
def update
@post = Post . find ( params [ :id ] )
authorize @post
if @post . update ( post_params )
redirect_to @post
else
render :edit
end
end
วิธีการอนุญาตอนุมานโดยอัตโนมัติว่า Post
จะมีคลาส PostPolicy
ที่ตรงกัน และสร้างอินสแตนซ์ของคลาสนี้ โดยส่งมอบผู้ใช้ปัจจุบันและบันทึกที่กำหนด จากนั้นอนุมานจากชื่อการกระทำว่าควรเรียก update?
ในกรณีของนโยบายนี้ ในกรณีนี้ คุณสามารถจินตนาการได้ว่า authorize
จะทำอะไรแบบนี้:
unless PostPolicy . new ( current_user , @post ) . update?
raise Pundit :: NotAuthorizedError , "not allowed to PostPolicy#update? this Post"
end
คุณสามารถส่งอาร์กิวเมนต์ที่สองเพื่อ authorize
หากชื่อของการอนุญาตที่คุณต้องการตรวจสอบไม่ตรงกับชื่อการกระทำ ตัวอย่างเช่น:
def publish
@post = Post . find ( params [ :id ] )
authorize @post , :update?
@post . publish!
redirect_to @post
end
คุณสามารถส่งผ่านอาร์กิวเมนต์เพื่อแทนที่คลาสนโยบายได้หากจำเป็น ตัวอย่างเช่น:
def create
@publication = find_publication # assume this method returns any model that behaves like a publication
# @publication.class => Post
authorize @publication , policy_class : PublicationPolicy
@publication . publish!
redirect_to @publication
end
หากคุณไม่มีอินสแตนซ์สำหรับอาร์กิวเมนต์แรกที่จะ authorize
คุณสามารถผ่านคลาสได้ ตัวอย่างเช่น:
นโยบาย:
class PostPolicy < ApplicationPolicy
def admin_list?
user . admin?
end
end
ตัวควบคุม:
def admin_list
authorize Post # we don't have a particular post to authorize
# Rest of controller action
end
authorize
ส่งคืนอินสแตนซ์ที่ส่งผ่านไป ดังนั้นคุณจึงสามารถเชื่อมโยงมันได้ดังนี้:
ตัวควบคุม:
def show
@user = authorize User . find ( params [ :id ] )
end
# return the record even for namespaced policies
def show
@user = authorize [ :admin , User . find ( params [ :id ] ) ]
end
คุณสามารถรับอินสแตนซ์ของนโยบายได้อย่างง่ายดายผ่านวิธี policy
ทั้งในมุมมองและตัวควบคุม สิ่งนี้มีประโยชน์อย่างยิ่งสำหรับการแสดงลิงก์หรือปุ่มตามเงื่อนไขในมุมมอง:
<% if policy(@post).update? %>
<%= link_to "Edit post", edit_post_path(@post) %>
<% end %>
เนื่องจากมีนโยบายที่ไม่มีโมเดล / คลาส Ruby ที่เกี่ยวข้อง คุณสามารถดึงข้อมูลได้โดยการส่งสัญลักษณ์
# app/policies/dashboard_policy.rb
class DashboardPolicy
attr_reader :user
# `_record` in this example will be :dashboard
def initialize ( user , _record )
@user = user
end
def show?
user . admin?
end
end
โปรดทราบว่านโยบายแบบไม่มีส่วนหัวยังคงต้องยอมรับข้อโต้แย้ง 2 ข้อ อาร์กิวเมนต์ที่สองจะเป็นสัญลักษณ์ :dashboard
ในกรณีนี้ ซึ่งเป็นสิ่งที่ถูกส่งผ่านเป็นบันทึกเพื่อ authorize
ด้านล่าง
# In controllers
def show
authorize :dashboard , :show?
...
end
# In views
<% if policy(:dashboard).show? %>
<%= link_to 'Dashboard', dashboard_path %>
<% end %>
บ่อยครั้ง คุณจะต้องการรายการบันทึกการดูบางประเภทที่ผู้ใช้รายใดรายหนึ่งสามารถเข้าถึงได้ เมื่อใช้ Pundit คุณจะต้องกำหนดคลาสที่เรียกว่าขอบเขตนโยบาย อาจมีลักษณะดังนี้:
class PostPolicy < ApplicationPolicy
class Scope
def initialize ( user , scope )
@user = user
@scope = scope
end
def resolve
if user . admin?
scope . all
else
scope . where ( published : true )
end
end
private
attr_reader :user , :scope
end
def update?
user . admin? or not record . published?
end
end
บัณฑิตตั้งสมมติฐานเกี่ยวกับชั้นเรียนนี้ดังต่อไปนี้:
Scope
และซ้อนอยู่ใต้คลาสนโยบายcurrent_user
เพื่อดึงสิ่งที่จะส่งเข้าสู่อาร์กิวเมนต์นี้ActiveRecord::Relation
แต่อาจเป็นอย่างอื่นทั้งหมดresolve
ซึ่งควรส่งคืนผลลัพธ์บางประเภทที่สามารถวนซ้ำได้ สำหรับคลาส ActiveRecord โดยปกติจะเป็น ActiveRecord::Relation
คุณอาจต้องการสืบทอดจากขอบเขตนโยบายแอปพลิเคชันที่สร้างโดยตัวสร้าง หรือสร้างคลาสพื้นฐานของคุณเองเพื่อสืบทอดจาก:
class PostPolicy < ApplicationPolicy
class Scope < ApplicationPolicy :: Scope
def resolve
if user . admin?
scope . all
else
scope . where ( published : true )
end
end
end
def update?
user . admin? or not record . published?
end
end
ตอนนี้คุณสามารถใช้คลาสนี้จากคอนโทรลเลอร์ของคุณผ่านทางวิธี policy_scope
:
def index
@posts = policy_scope ( Post )
end
def show
@post = policy_scope ( Post ) . find ( params [ :id ] )
end
เช่นเดียวกับวิธีการอนุญาต คุณยังสามารถแทนที่คลาสขอบเขตนโยบายได้:
def index
# publication_class => Post
@publications = policy_scope ( publication_class , policy_scope_class : PublicationPolicy :: Scope )
end
ในกรณีนี้เป็นทางลัดในการทำ:
def index
@publications = PublicationPolicy :: Scope . new ( current_user , Post ) . resolve
end
คุณสามารถและได้รับการสนับสนุนให้ใช้วิธีนี้ในมุมมอง:
<% policy_scope(@user.posts).each do |post| %>
< p > <%= link_to post . title , post_path ( post ) %> </ p >
<% end %>
เมื่อคุณพัฒนาแอปพลิเคชันกับ Pundit อาจเป็นเรื่องง่ายที่จะลืมอนุญาตการดำเนินการบางอย่าง คนก็ขี้ลืมไปซะหมด เนื่องจาก Pundit สนับสนุนให้คุณเพิ่ม authorize
การโทรด้วยตนเองให้กับแต่ละการทำงานของคอนโทรลเลอร์ จึงเป็นเรื่องง่ายมากที่จะพลาดการกระทำดังกล่าว
โชคดีที่ Pundit มีฟีเจอร์ที่มีประโยชน์ซึ่งจะเตือนคุณในกรณีที่คุณลืม Pundit ติดตามว่าคุณได้เรียก authorize
ที่ใดก็ได้ในการดำเนินการควบคุมของคุณหรือไม่ Pundit ยังเพิ่มวิธีการให้กับคอนโทรลเลอร์ของคุณที่เรียกว่า verify_authorized
วิธีนี้จะทำให้เกิดข้อยกเว้นหากยังไม่ได้เรียก authorize
คุณควรเรียกใช้เมธอดนี้ใน after_action
hook เพื่อให้แน่ใจว่าคุณไม่ลืมให้สิทธิ์การดำเนินการ ตัวอย่างเช่น:
class ApplicationController < ActionController :: Base
include Pundit :: Authorization
after_action :verify_authorized
end
ในทำนองเดียวกัน Pundit ยังเพิ่ม verify_policy_scoped
ให้กับคอนโทรลเลอร์ของคุณด้วย สิ่งนี้จะทำให้เกิดข้อยกเว้นที่คล้ายกับ verify_authorized
อย่างไรก็ตาม จะติดตามว่ามีการใช้ policy_scope
แทน authorize
หรือไม่ สิ่งนี้มีประโยชน์เป็นส่วนใหญ่สำหรับการดำเนินการของตัวควบคุม เช่น index
ซึ่งค้นหาคอลเลกชันที่มีขอบเขตและไม่อนุญาตให้แต่ละอินสแตนซ์
class ApplicationController < ActionController :: Base
include Pundit :: Authorization
after_action :verify_pundit_authorization
def verify_pundit_authorization
if action_name == "index"
verify_policy_scoped
else
verify_authorized
end
end
end
กลไกการตรวจสอบนี้มีไว้เพื่อช่วยเหลือคุณในการพัฒนาแอปพลิเคชันของคุณเท่านั้น ดังนั้นอย่าลืมโทรไป authorize
ไม่ใช่กลไกป้องกันข้อผิดพลาดหรือกลไกการอนุญาตบางประเภท คุณควรจะสามารถลบตัวกรองเหล่านี้ได้โดยไม่ส่งผลกระทบต่อวิธีการทำงานของแอปของคุณแต่อย่างใด
บางคนพบว่าคุณลักษณะนี้น่าสับสน ในขณะที่คนอื่นๆ อีกหลายคนพบว่ามีประโยชน์อย่างยิ่ง หากคุณตกอยู่ในประเภทของคนที่พบว่ามันสับสน คุณก็ไม่จำเป็นต้องใช้มัน Pundit จะทำงานได้ดีโดยไม่ต้องใช้ verify_authorized
และ verify_policy_scoped
หากคุณใช้ verify_authorized
ในคอนโทรลเลอร์ของคุณ แต่จำเป็นต้องข้ามการตรวจสอบแบบมีเงื่อนไข คุณสามารถใช้ skip_authorization
หากต้องการเลี่ยงผ่าน verify_policy_scoped
ให้ใช้ skip_policy_scope
สิ่งเหล่านี้มีประโยชน์ในสถานการณ์ที่คุณไม่ต้องการปิดการใช้งานการยืนยันสำหรับการดำเนินการทั้งหมด แต่มีบางกรณีที่คุณตั้งใจที่จะไม่อนุญาต
def show
record = Record . find_by ( attribute : "value" )
if record . present?
authorize record
else
skip_authorization
end
end
บางครั้งคุณอาจต้องการประกาศอย่างชัดเจนว่าจะใช้นโยบายใดสำหรับคลาสที่กำหนด แทนที่จะปล่อยให้ผู้เชี่ยวชาญอนุมานได้ ซึ่งสามารถทำได้ดังนี้:
class Post
def self . policy_class
PostablePolicy
end
end
หรือคุณสามารถประกาศวิธีการอินสแตนซ์:
class Post
def policy_class
PostablePolicy
end
end
Pundit เป็นห้องสมุดขนาดเล็กที่มีจุดประสงค์เฉพาะ และไม่ได้ทำอะไรที่คุณทำเองไม่ได้ ไม่มีซอสลับที่นี่ มันทำน้อยที่สุดเท่าที่จะเป็นไปได้ แล้วก็หลีกทางให้คุณ
ด้วยตัวช่วยเพียงไม่กี่ตัวแต่ทรงพลังที่มีอยู่ใน Pundit คุณจะมีพลังในการสร้างระบบการอนุญาตที่มีโครงสร้างที่ดีและทำงานได้อย่างสมบูรณ์โดยไม่ต้องใช้ DSL พิเศษหรือไวยากรณ์ที่เก๋ไก๋
โปรดจำไว้ว่าคลาสนโยบายและขอบเขตทั้งหมดนั้นเป็นคลาส Ruby ธรรมดา ซึ่งหมายความว่าคุณสามารถใช้กลไกเดียวกับที่คุณใช้เพื่อทำให้สิ่งต่าง ๆ แห้งอยู่เสมอ สรุปชุดสิทธิ์ลงในโมดูลและรวมไว้ในนโยบายต่างๆ ใช้ alias_method
เพื่อให้สิทธิ์บางอย่างทำงานเหมือนกับสิทธิ์อื่นๆ สืบทอดจากชุดสิทธิ์พื้นฐาน ใช้โปรแกรมเมตาหากคุณจำเป็นจริงๆ
ใช้ตัวสร้างที่ให้มาเพื่อสร้างนโยบาย:
rails g pundit:policy post
ในแอปพลิเคชั่นจำนวนมาก เฉพาะผู้ใช้ที่เข้าสู่ระบบเท่านั้นที่สามารถทำอะไรได้จริงๆ หากคุณกำลังสร้างระบบดังกล่าว อาจเป็นเรื่องยุ่งยากในการตรวจสอบว่าผู้ใช้ในนโยบายไม่ใช่ nil
สำหรับการอนุญาตทุกครั้ง นอกเหนือจากนโยบายแล้ว คุณสามารถเพิ่มการตรวจสอบนี้ไปยังคลาสพื้นฐานเพื่อดูขอบเขตได้
เราขอแนะนำให้คุณกำหนดตัวกรองที่เปลี่ยนเส้นทางผู้ใช้ที่ไม่ได้รับการรับรองความถูกต้องไปยังหน้าเข้าสู่ระบบ เพื่อเป็นการป้องกันรอง หากคุณได้กำหนด ApplicationPolicy ไว้ อาจเป็นความคิดที่ดีที่จะยกข้อยกเว้นขึ้นหากผู้ใช้ที่ไม่ได้รับการรับรองความถูกต้องผ่านเข้ามาได้ ด้วยวิธีนี้คุณสามารถล้มเหลวได้อย่างสง่างามมากขึ้น
class ApplicationPolicy
def initialize ( user , record )
raise Pundit :: NotAuthorizedError , "must be logged in" unless user
@user = user
@record = record
end
class Scope
attr_reader :user , :scope
def initialize ( user , scope )
raise Pundit :: NotAuthorizedError , "must be logged in" unless user
@user = user
@scope = scope
end
end
end
เพื่อรองรับรูปแบบวัตถุ null คุณอาจพบว่าคุณต้องการใช้ NilClassPolicy
สิ่งนี้อาจมีประโยชน์ในกรณีที่คุณต้องการขยาย ApplicationPolicy ของคุณเพื่อให้ยอมรับได้ ตัวอย่างเช่น การเชื่อมโยงที่อาจเป็น nil
class NilClassPolicy < ApplicationPolicy
class Scope < ApplicationPolicy :: Scope
def resolve
raise Pundit :: NotDefinedError , "Cannot scope NilClass"
end
end
def show?
false # Nobody can see nothing
end
end
Pundit ยก Pundit::NotAuthorizedError
ที่คุณสามารถ Rescue_from ใน ApplicationController
ของคุณ คุณสามารถปรับแต่งเมธอด user_not_authorized
ในคอนโทรลเลอร์ทุกตัว
class ApplicationController < ActionController :: Base
include Pundit :: Authorization
rescue_from Pundit :: NotAuthorizedError , with : :user_not_authorized
private
def user_not_authorized
flash [ :alert ] = "You are not authorized to perform this action."
redirect_back_or_to ( root_path )
end
end
หรือคุณสามารถจัดการ Pundit::NotAuthorizedError's ได้ทั่วโลกโดยให้ Rails จัดการพวกมันเป็นข้อผิดพลาด 403 และให้บริการหน้าข้อผิดพลาด 403 เพิ่มสิ่งต่อไปนี้ใน application.rb:
config.action_dispatch.rescue_responses["Pundit::NotAuthorizedError"] = :forbidden
NotAuthorizedError
ให้ข้อมูลเกี่ยวกับแบบสอบถามใด (เช่น :create?
) บันทึกใด (เช่น อินสแตนซ์ของ Post
) และนโยบายใด (เช่น อินสแตนซ์ของ PostPolicy
) ที่ทำให้เกิดข้อผิดพลาด
วิธีหนึ่งในการใช้คุณสมบัติ query
record
และ policy
เหล่านี้คือการเชื่อมต่อกับ I18n
เพื่อสร้างข้อความแสดงข้อผิดพลาด ต่อไปนี้คือวิธีที่คุณอาจดำเนินการดังกล่าว
class ApplicationController < ActionController :: Base
rescue_from Pundit :: NotAuthorizedError , with : :user_not_authorized
private
def user_not_authorized ( exception )
policy_name = exception . policy . class . to_s . underscore
flash [ :error ] = t " #{ policy_name } . #{ exception . query } " , scope : "pundit" , default : :default
redirect_back_or_to ( root_path )
end
end
en :
pundit :
default : ' You cannot perform this action. '
post_policy :
update? : ' You cannot edit this post! '
create? : ' You cannot create posts! '
นี่คือตัวอย่าง ผู้เชี่ยวชาญไม่เชื่อเรื่องวิธีที่คุณใช้ข้อความแสดงข้อผิดพลาด
บางครั้งคุณต้องการดึงนโยบายสำหรับเรกคอร์ดภายนอกตัวควบคุมหรือมุมมอง ตัวอย่างเช่น เมื่อคุณมอบหมายสิทธิ์จากนโยบายหนึ่งไปยังอีกนโยบายหนึ่ง
คุณสามารถดึงข้อมูลนโยบายและขอบเขตเช่นนี้ได้อย่างง่ายดาย:
Pundit . policy! ( user , post )
Pundit . policy ( user , post )
Pundit . policy_scope! ( user , Post )
Pundit . policy_scope ( user , Post )
วิธีการ Bang จะทำให้เกิดข้อยกเว้นหากไม่มีนโยบาย ในขณะที่วิธีที่ไม่มี Bang จะคืนค่าเป็นศูนย์
ในบางครั้ง คอนโทรลเลอร์ของคุณอาจไม่สามารถเข้าถึง current_user
หรือวิธีการที่ Pundit ควรเรียกใช้อาจไม่ใช่ current_user
เพื่อแก้ไขปัญหานี้ คุณสามารถกำหนดวิธีการในคอนโทรลเลอร์ของคุณชื่อ pundit_user
def pundit_user
User . find_by_other_means
end
ในบางกรณี การมีนโยบายหลายนโยบายที่ให้บริการบริบทที่แตกต่างกันสำหรับทรัพยากรอาจเป็นประโยชน์ ตัวอย่างที่ชัดเจนของกรณีนี้คือกรณีที่นโยบายผู้ใช้แตกต่างจากนโยบายผู้ดูแลระบบ หากต้องการอนุญาตด้วยนโยบายเนมสเปซ ให้ส่งเนมสเปซไปยังตัวช่วย authorize
ในอาร์เรย์:
authorize ( post ) # => will look for a PostPolicy
authorize ( [ :admin , post ] ) # => will look for an Admin::PostPolicy
authorize ( [ :foo , :bar , post ] ) # => will look for a Foo::Bar::PostPolicy
policy_scope ( Post ) # => will look for a PostPolicy::Scope
policy_scope ( [ :admin , Post ] ) # => will look for an Admin::PostPolicy::Scope
policy_scope ( [ :foo , :bar , Post ] ) # => will look for a Foo::Bar::PostPolicy::Scope
หากคุณใช้นโยบายเนมสเปซสำหรับบางอย่างเช่น มุมมองผู้ดูแลระบบ จะมีประโยชน์ในการแทนที่ policy_scope
และ authorize
ผู้ช่วยเหลือใน AdminController
ของคุณใช้เนมสเปซโดยอัตโนมัติ:
class AdminController < ApplicationController
def policy_scope ( scope )
super ( [ :admin , scope ] )
end
def authorize ( record , query = nil )
super ( [ :admin , record ] , query )
end
end
class Admin :: PostController < AdminController
def index
policy_scope ( Post )
end
def show
post = authorize Post . find ( params [ :id ] )
end
end
Pundit ขอแนะนำให้คุณสร้างโมเดลแอปพลิเคชันของคุณในลักษณะที่บริบทเดียวที่คุณต้องการสำหรับการอนุญาตคือออบเจ็กต์ผู้ใช้และโมเดลโดเมนที่คุณต้องการตรวจสอบการอนุญาต หากคุณพบว่าตัวเองต้องการบริบทมากกว่านั้น ให้พิจารณาว่าคุณกำลังอนุญาตโมเดลโดเมนที่ถูกต้องหรือไม่ บางทีโมเดลโดเมนอื่น (หรือ wrapper รอบโดเมนหลายโมเดล) สามารถให้บริบทที่คุณต้องการได้
บัณฑิตไม่อนุญาตให้คุณส่งข้อโต้แย้งเพิ่มเติมไปยังนโยบายด้วยเหตุผลนี้
อย่างไรก็ตาม ในกรณีที่เกิดขึ้นไม่บ่อยนัก คุณอาจต้องให้สิทธิ์ตามบริบทมากกว่าผู้ใช้ที่ได้รับการตรวจสอบสิทธิ์ในปัจจุบัน สมมติว่าการให้สิทธิ์ขึ้นอยู่กับที่อยู่ IP นอกเหนือจากผู้ใช้ที่ได้รับการตรวจสอบสิทธิ์ ในกรณีดังกล่าว ทางเลือกหนึ่งคือสร้างคลาสพิเศษที่รวมทั้งผู้ใช้และ IP แล้วส่งต่อไปยังนโยบาย
class UserContext
attr_reader :user , :ip
def initialize ( user , ip )
@user = user
@ip = ip
end
end
class ApplicationController
include Pundit :: Authorization
def pundit_user
UserContext . new ( current_user , request . ip )
end
end
ใน Rails การป้องกันการกำหนดมวลได้รับการจัดการในคอนโทรลเลอร์ ด้วย Pundit คุณสามารถควบคุมแอตทริบิวต์ที่ผู้ใช้สามารถเข้าถึงได้เพื่ออัปเดตผ่านนโยบายของคุณ คุณสามารถตั้งค่าวิธี permitted_attributes
ในนโยบายของคุณได้ดังนี้:
# app/policies/post_policy.rb
class PostPolicy < ApplicationPolicy
def permitted_attributes
if user . admin? || user . owner_of? ( post )
[ :title , :body , :tag_list ]
else
[ :tag_list ]
end
end
end
ตอนนี้คุณสามารถดึงข้อมูลแอตทริบิวต์เหล่านี้ได้จากนโยบาย:
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
def update
@post = Post . find ( params [ :id ] )
if @post . update ( post_params )
redirect_to @post
else
render :edit
end
end
private
def post_params
params . require ( :post ) . permit ( policy ( @post ) . permitted_attributes )
end
end
อย่างไรก็ตาม นี่เป็นเรื่องยุ่งยากเล็กน้อย ดังนั้น Pundit จึงมีวิธีช่วยเหลือที่สะดวก:
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
def update
@post = Post . find ( params [ :id ] )
if @post . update ( permitted_attributes ( @post ) )
redirect_to @post
else
render :edit
end
end
end
หากคุณต้องการอนุญาตแอตทริบิวต์ที่แตกต่างกันตามการกระทำปัจจุบัน คุณสามารถกำหนดเมธอด permitted_attributes_for_#{action}
ในนโยบายของคุณได้:
# app/policies/post_policy.rb
class PostPolicy < ApplicationPolicy
def permitted_attributes_for_create
[ :title , :body ]
end
def permitted_attributes_for_edit
[ :body ]
end
end
หากคุณได้กำหนดวิธีการเฉพาะการดำเนินการในนโยบายของคุณสำหรับการดำเนินการปัจจุบัน ตัวช่วย permitted_attributes
จะเรียกมันว่า o