【问题标题】:Rails: No Pundit policy found in RailsRails:在 Rails 中找不到 Pundit 政策
【发布时间】:2016-08-14 18:13:43
【问题描述】:

我以前用过Pundit Gem,但我从来没有尝试过我现在想做的事情,出于某种原因,Pundit 不高兴。

我的目标是在我的“索引”(Foos)页面上创建一个带有“创建”(Foo)表单的模式。因此,我需要实例化一个空的 Foo 对象以使模态表单起作用。

我遇到的问题是 Pundit 在我远程提交表单时抛出错误。错误是:

Pundit::NotDefinedError - 找不到 nil 策略

我试图了解为什么会发生这种情况,但我还没有解决它。

这是我的 foos_controller.rb#index:

...
def index
  @foo = Foo.new
  authorize @foo, :new?
  @foos = policy_scope(Foo)
end
...

然后我有以下 'before_action' 过滤器运行我的其他操作,即“创建”

...
before_action     :run_authorisation_check, except: [:index]
def run_authorisation_check
  authorize @foo
end
...

我在 foo_policy.rb 中使用的策略:

....
def index?
  user.has_any_role? :super_admin
end

def create?
  user.has_any_role? :super_admin
end

def new?
  create?
end


def scope
  Pundit.policy_scope!(user, record.class)
end

class Scope
  attr_reader :user, :scope

  def initialize(user, scope)
    @user = user
    @scope = scope
  end

  def resolve
    if user.has_any_role? :super_admin
      scope.all
    end
  end
end
....

在我提交表单之前,错误不会出现。任何熟悉 Pundit 的人都可以帮助指导我了解我做错了什么吗?

更新

完整的 foos_controller.rb

class FoosController < ApplicationController


  def index
    @foo = Foo.new
    authorize @foo, :create?
    @foos = policy_scope(Foo)
  end


  def new
    @foo = Foo.new
  end


  def create
    @foo = Foo.new(foo_params)
    respond_to do |format|
      if @foo.save
        flash[:notice] = I18n.t("foo.flash.created")
        format.json { render json: @foo, status: :ok }
      else
        format.json { render json: @foo.errors, status: :unprocessable_entity }
      end
    end
  end

  private

    before_action     :run_authorisation_check, except: [:index]

    def foo_params
      params.fetch(:foo, {}).permit(:bar)
    end

    def run_authorisation_check
      authorize @foo
    end
end

【问题讨论】:

  • 看来你可能没有设置@foo的值,在调用:run_authorisation_check方法之前,你能展示你完整的控制器吗?
  • @oreoluwa 我更新了我的问题

标签: ruby-on-rails ruby-on-rails-5 pundit


【解决方案1】:

是的,您没有设置 @foo 的值,这就是您收到错误 unable to find policy of nil 的原因。

大多数时候,您的 foos_controller.rb 中会包含这样的内容:

before_action :set_foo, only: [:show, :edit, :update, :destroy]
before_action     :run_authorisation_check, except: [:index]
...
private
  def set_foo
    @foo = Foo.find(params[:id])
  end

让我知道这是否有效

【讨论】:

  • 如何使用 :new 或 :create 方法设置 @foo?这通常需要一个空实例......或者我完全错过了什么?
【解决方案2】:

我在使用 Pundit gem 处理仅 Rails 6 API 的应用程序时遇到了这个问题。

我在测试 Pundit 对控制器操作的授权时遇到了以下错误:

Pundit::NotDefinedError - 无法找到 nil 的策略

我是这样解决的

假设我有一个名为 SchoolPolicy 的策略:

class SchoolPolicy < ApplicationPolicy
  attr_reader :user, :school

  def initialize(user, school)
    @user = user
    @school = school
  end

  def index?
    user.admin?
  end

  def show?
    user.admin?
  end

  def new
    create?
  end

  def edit
    update?
  end

  def create
    user.admin?
  end

  def update?
    user.admin?
  end

  def destroy?
    user.admin?
  end
end

然后在我的SchoolsController 中,我将有以下内容:

class Api::V1::SchoolsController < ApplicationController
  before_action :set_school, only: [:show, :update, :destroy]
  after_action :verify_authorized, except: :show

  # GET /schools
  def index
    @schools = School.all
    authorize @schools

    render json: SchoolSerializer.new(@schools).serializable_hash.to_json
  end

  # GET /schools/1
  def show

    render json: SchoolSerializer.new(@school).serializable_hash.to_json
  end

  # POST /schools
  def create
    @school = School.new(school_params)
    authorize @school

    if @school.save
      render json: SchoolSerializer.new(@school).serializable_hash.to_json, status: :created, location: api_v1_school_url(@school)
    else
      render json: @school.errors, status: :unprocessable_entity
    end
  end

  # PATCH/PUT /schools/1
  def update
    authorize @school

    if @school.update(school_params)
      render json: SchoolSerializer.new(@school).serializable_hash.to_json
    else
      render json: @school.errors, status: :unprocessable_entity
    end
  end

  # DELETE /schools/1
  def destroy
    authorize @school

    @school.destroy
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_school
      @school = School.find(params[:id])
    end

    # Only allow a trusted parameter "white list" through.
    def school_params
      params.require(:school).permit(:name, :alias, :code)
    end
end

注意

  1. 我使用after_action 回调调用verify_authorized 方法来强制对控制器操作进行授权
  2. 我没有在 show 操作上调用 authorize 方法,因为根据我的设计,我出于授权选择跳过了该方法。
  3. authorize 方法调用的实例变量对应于被调用的控制器动作的实例变量。因此,index 操作为 @schoolscreate 操作为 @school,以此类推。

就是这样。

我希望这会有所帮助

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多