在 Pundit 中实施范围 [英] Implementing scopes in Pundit

查看:62
本文介绍了在 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆