在 Pundit 中实施范围 [英] Implementing scopes in Pundit
问题描述
我正在使用 Pundit gem(带有 Devise 和 Rolify)来限制对基于登录用户角色的信息的访问.
I am using the Pundit gem (with Devise and Rolify) to restrict access to information based on logged-in user roles.
此时,我为我的用户模型定义了三个角色:管理员、客户管理员和客户管理员.
At this time I have three roles for my User model defined: Admin, Client Admin, and Customer Admin.
用户属于客户.客户有_许多用户.
A User belongs_to a Customer. Customer has_many Users.
我在索引客户模型时成功实施了 Pundit 政策.管理员和客户管理员可以看到所有客户.客户管理员只能看到他们自己的记录.
I have successfully implemented a Pundit policy when indexing the Customer model. Admins and Client Admins can see all Customers. Customer Admin can only see their OWN record.
问题出在我试图限制 Customer 控制器的 show 方法时.管理员和客户管理员可以看到所有客户.但是,客户管理员应该只能看到他自己的记录.但就目前而言,客户管理员可以在 URL 中输入任何 ID 并查看任何客户记录.
The problem lies when I am trying to restrict the show method of the Customer controller. Admins and Client Admins can see all Customers. However, the Customer Admin should only be able to see his own record. But as it stands the Customer Admin can input any id in the URL and see any Customer record.
我对范围界定很模糊.我的理解是,政策方法(即索引?和展示?)是限制 WHO 可以执行这些操作,而范围方法限制可以获取哪些记录.我在为上述场景编写正确的范围时遇到问题.
I'm fuzzy on the scoping. It's my understanding that the Policy methods (i.e. index? and show?) are to restrict WHO can perform these actions and the Scoping methods restrict WHICH RECORDS can be obtained. I'm having trouble composing the correct scope for the above scenario.
这是客户控制器:
class CustomersController < ApplicationController
before_action :set_customer, only: [:show, :edit, :update, :destroy]
after_action :verify_authorized
# GET /customers
# GET /customers.json
def index
@customers = policy_scope(Customer)
authorize Customer
end
# GET /customers/1
# GET /customers/1.json
def show
authorize @customer
end
# GET /customers/new
def new
@customer = Customer.new
authorize @customer
end
# GET /customers/1/edit
def edit
authorize @customer
end
# POST /customers
# POST /customers.json
def create
@customer = Customer.new(customer_params)
authorize @customer
respond_to do |format|
if @customer.save
format.html { redirect_to @customer, notice: 'Customer was successfully created.' }
format.json { render :show, status: :created, location: @customer }
else
format.html { render :new }
format.json { render json: @customer.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /customers/1
# PATCH/PUT /customers/1.json
def update
authorize @customer
respond_to do |format|
if @customer.update(customer_params)
format.html { redirect_to @customer, notice: 'Customer was successfully updated.' }
format.json { render :show, status: :ok, location: @customer }
else
format.html { render :edit }
format.json { render json: @customer.errors, status: :unprocessable_entity }
end
end
end
# DELETE /customers/1
# DELETE /customers/1.json
def destroy
authorize @customer
@customer.destroy
respond_to do |format|
format.html { redirect_to customers_url, notice: 'Customer was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_customer
@customer = Customer.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def customer_params
params.require(:customer).permit(:name, :parent_customer_id, :customer_type, :active, :currency)
end
end
这是客户政策:
class CustomerPolicy < ApplicationPolicy
def index?
# Admins, ClientAdmins, and CustomerAdmins can index customers (see Scope class for filters)
@user.has_role? :admin or @user.has_role? :client_admin or @user.has_role? :customer_admin
end
def show?
# Admins, ClientAdmins, and CustomerAdmins can see any customer details
@user.has_role? :admin or @user.has_role? :client_admin or @user.has_role? :customer_admin
end
def update?
# Only Admins and ClientAdmins can update customer details
@user.has_role? :admin or @user.has_role? :client_admin
end
def destroy?
@user.has_role? :admin or @user.has_role? :client_admin
end
class Scope < Struct.new(:user, :scope)
def resolve
if (user.has_role? :admin or user.has_role? :client_admin)
# Admins and ClientAdmins can see all Customers
scope.where(:parent_id => nil)
elsif user.has_role? :customer_admin
# Customer Admins can only see their own Customer
scope.where(:id => user.customer) # THIS DOES NOT APPEAR TO GET INVOKED BY THE SHOW METHOD OF THE CONTROLLER
end
end
def show?
# NOT SURE WHAT TO PUT IN HERE
end
end
end
<小时>
成功!!感谢 railscard 给我的先机,诀窍是修改节目?客户政策文件中的方法,如下所示:
Success!! Thanks to the headstart given to me by railscard, the trick was to modify the show? method in the Customer policy file like the following:
def show?
# Admins, ClientAdmins, and CustomerAdmins can see any customer details
# Students cannot see customer details
return true if user.has_role?(:admin) || user.has_role?(:client_admin)
return true if user.customer_id == @record.id && user.has_role?(:customer_admin)
false
end
请注意,我必须使用 @record 实例变量,因为应用程序策略类使用它来引用由授权方法传入的记录.
Note that I had to use the @record instance variable, as that's what the Application policy class uses to refer to the record being passed in by the authorize method.
谢谢!!
推荐答案
我认为您不需要范围来限制对 show
操作的访问.
I think you don't need scope to restrict access for show
action.
def show?
return true if user.has_role? :admin || user.has_role? :client_admin
return true if user.customer_id == customer.id && user.has_role? :customer_admin
false
end
Pundit 范围通常用于获取用户有权访问的记录列表.对于 show
方法(或控制器中的任何其他方法,您调用 authorize
),Pundit 使用当前用户和给定客户实例化策略类,然后简单地调用 show?
检查用户权限的方法,即CustomerPolicy.new(current_user, @customer).show?
Pundit scopes usually used to fetch a list of records which user have access to. In case of show
method (or any other method in controller, where you call authorize
) Pundit instantiates policy class with current user and given customer and then simply calls show?
method to check user permissions, i.e. CustomerPolicy.new(current_user, @customer).show?
这篇关于在 Pundit 中实施范围的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!