Что такое СУХОЙ способ ограничить весь контроллер с помощью Pundit в Rails? - PullRequest
0 голосов
/ 06 февраля 2019

Я использую 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

Есть ли способ сделать это, что я не понимаю, или это как Punditразработан, чтобы потребовать, чтобы вы были явными?

Ответы [ 3 ]

0 голосов
/ 06 февраля 2019

Pundit не требует, чтобы вы были явными, но это позволяет.Если метод index? в вашей политике не был дублирован, вы бы хотели, чтобы возможность была явной.

Вы можете начать с рассмотрения перемещения некоторых проверок авторизации в метод set_material,сокращает более половины проверок.

Другая половина может быть абстрагирована в другие частные методы, если вы хотите, но я думаю, что они хороши как есть.

Вы также можете посмотретьпри добавлении обратного вызова before_action для вызова авторизатора на основе имени действия после того, как вы запомнили @material с помощью другого обратного вызова, но читаемость, вероятно, пострадает.

0 голосов
/ 06 февраля 2019

Первый шаг - просто переместить вызов для авторизации на ваш обратный вызов:

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

Вы также можете написать @material = authorize Material.find(params[:id]), если ваша версия Pundit актуальна (предыдущие версии вместо этого возвращали true / falseзаписи).

Pundit обладает огромной гибкостью в выборе способа его использования.Например, вы можете создать отдельную отдельную политику без заголовка :

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

Или вы можетепросто используйте наследование или mixins, чтобы высушить ваш класс политики:

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

Pundit - это всего лишь обычный старый Ruby OOP.

0 голосов
/ 06 февраля 2019

Используйте второй аргумент для метода authorize.Например:

authorize @material, :index?

Теперь вы можете удалить все другие методы, которые просто вызывают index?

...