代码之家  ›  专栏  ›  技术社区  ›  Lee McAlilly

在Rails中使用Pundit限制整个控制器的枯燥方法是什么?

  •  2
  • Lee McAlilly  · 技术社区  · 6 年前

    Pundit 有了Rails,我就有了一个控制器,我需要将它完全限制在特定的用户角色中。我的角色是“员工”和“消费者”。员工应该可以完全访问控制器,但消费者应该没有访问权限。

    例如,我的政策如下:

    class MaterialPolicy < ApplicationPolicy
      attr_reader :user, :material
    
      def initialize(user, material)
        @user     = user
        @material = material
      end
    
      def index?
        user.staff?
      end
    
      def show?
        index?
      end
    
      def new?
        index?
      end
    
      def edit?
        index?
      end
    
      def create?
        index?
      end
    
      def update?
        create?
      end
    
      def destroy?
        update?
      end
    end
    

    和我的控制器:

    class MaterialsController < ApplicationController
      before_action :set_material, only: [:show, :edit, :update, :destroy]
    
      # GET /materials
      def index
        @materials = Material.all
        authorize @materials
      end
    
      # GET /materials/1
      def show
        authorize @material
      end
    
      # GET /materials/new
      def new
        @material = Material.new
        authorize @material
      end
    
      # GET /materials/1/edit
      def edit
        authorize @material
      end
    
      # POST /materials
      def create
        @material = Material.new(material_params)
        authorize @material
    
        respond_to do |format|
          if @material.save
            format.html { redirect_to @material, notice: 'Material was successfully created.' }
          else
            format.html { render :new }
          end
        end
      end
    
      # PATCH/PUT /materials/1
      def update
        authorize @material
        respond_to do |format|
          if @material.update(material_params)
            format.html { redirect_to @material, notice: 'Material was successfully updated.' }
          else
            format.html { render :edit }
          end
        end
      end
    
      # DELETE /materials/1
      def destroy
        authorize @material
        @material.destroy
        respond_to do |format|
          format.html { redirect_to materials_url, notice: 'Material was successfully destroyed.' }
        end
      end
    
      private
        # Use callbacks to share common setup or constraints between actions.
        def set_material
          @material = Material.find(params[:id])
        end
    
        # Never trust parameters from the scary internet, only allow the white list through.
        def material_params
          params.require(:material).permit(:name)
        end
    end
    

    有没有一种我不理解的方法,或者这就是权威的设计方式,要求你明确?

    3 回复  |  直到 6 年前
        1
  •  5
  •   max Mike Williams    6 年前

    第一步只是将要授权的调用移动到回调:

    def set_material
      @material = Material.find(params[:id])
      authorize @material
    end
    

    你也可以写 @material = authorize Material.find(params[:id])

    Pundit在您选择如何使用它方面有很大的灵活性。例如,您可以创建一个 separate headless policy :

    class StaffPolicy < ApplicationPolicy
      # the second argument is just a symbol (:staff) and is not actually used
      def initialize(user, symbol)
        @user = user
      end
      def access?
        user.staff?
      end
    end
    

    然后在回调中使用它来授权整个控制器:

    class MaterialsController < ApplicationController
      before_action :authorize_staff
      # ...
    
      def authorize_staff
        authorize :staff, :access?
      end
    end
    

    或者,您可以使用继承或混合来干燥策略类:

    class StaffPolicy < ApplicationPolicy
      %i[ show? index? new? create? edit? update? delete? ].each do |name|
        define_method name do
          user.staff?
        end
      end
    end
    
    class MaterialPolicy < StaffPolicy
      # this is how you would add additional restraints in a subclass
      def show?
        super && some_other_condition
      end
    end
    

    专家毕竟只是普通的老Ruby OOP。

        2
  •  1
  •   Jay Dorsey    6 年前

    权威并不要求你明确,但它允许你这样做。如果 index?

    您可以从将一些授权检查移动到 set_material 方法,它减少了一半以上的检查。

    如果您愿意,另一半可以抽象为其他私有方法,但我认为它们仍然可以。

    您还可以考虑添加一个before_操作回调,以便在您记录之后根据操作名称调用授权人 @material

        3
  •  0
  •   Raj    6 年前

    authorize 方法如:

    authorize @material, :index?
    

    现在可以删除所有其他只调用 index?