【问题标题】:NoMethodError undefined method `admin?' for nil:NilClass Pundit, Devise RailsNoMethodError 未定义方法“管理员?”对于 nil:NilClass Pundit,Devise Rails
【发布时间】:2017-11-06 04:01:39
【问题描述】:

我正在尝试将权威人士与我的活跃管理员集成并设计配置。但是该应用程序的工作方式很奇怪。它将模型/记录作为 current_user。

我的政策文件:

class AdminUserPolicy
  attr_reader :current_user, :model

  def initialize(current_user, model)
    Rails.logger.info '--------------- initialize called-------------------'
    Rails.logger.info current_user
    Rails.logger.info model
    @current_user = current_user
    @record = model
  end

  def index?
    @current_user.admin?
  end
end

控制器:

controller do
    include Pundit
    protect_from_forgery
    rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
    before_action :authenticate_admin_user!

    def index
        authorize current_admin_user
        super
    end

    private

        def user_not_authorized
           flash[:alert]="Access denied"
           redirect_to (request.referrer || admin_root_path)
        end
end

日志如下:

--------------- initialize called----------------------------------------

#<AdminUser:0x007f27733f8a80>
Completed 500 Internal Server Error in 364ms (ActiveRecord: 312.8ms)



NoMethodError (undefined method `admin?' for nil:NilClass):

app/policies/admin_user_policy.rb:13:in `index?'
app/admin/dashboard.rb:19:in `index'
  Rendering /usr/local/rvm/gems/ruby-2.3.4/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout
  Rendering /usr/local/rvm/gems/ruby-2.3.4/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/_source.html.erb
  Rendered /usr/local/rvm/gems/ruby-2.3.4/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/_source.html.erb (4.8ms)
  Rendering /usr/local/rvm/gems/ruby-2.3.4/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb
  Rendered /usr/local/rvm/gems/ruby-2.3.4/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (2.5ms)
  Rendering /usr/local/rvm/gems/ruby-2.3.4/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb
  Rendered /usr/local/rvm/gems/ruby-2.3.4/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (1.1ms)
  Rendered /usr/local/rvm/gems/ruby-2.3.4/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout (39.5ms)

根据日志,奇怪的是current_user=nilmodel=#&lt;AdminUser:0x007f27733f8a80&gt;

我将 current_user 与我的策略文件的模型交换为

def index?
  @record.admin?
end

而且它有效! 我不明白这种奇怪的行为。

【问题讨论】:

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


    【解决方案1】:

    Pundit policy doc 说它调用 current_user 方法来检索要发送到 Policy 类中 initialize 方法的第一个参数的内容。如果您已将 ActiveAdmin 配置为使用 current_admin_user 检索当前登录的用户,那么您必须像这样覆盖 ApplicationController 类中的权威默认方法:Ref

    class ApplicationController < ActionController::Base
      // ...
      def pundit_user
        current_admin_user // or whatever based on ActiveAdmin initializer config
      end
    end
    

    为了使定义的策略起作用,您必须在控制器操作中使用相应策略模型的实例调用authorize。因此,如果您有PostPolicy 并且想要授权update 操作,则必须执行以下操作:

    controller do
      def update
        @post = Post.find(params[:id])
        authorize @post // the current user will be automatically sent to the PostPolicy
        super
      end
    end
    

    authorize 方法自动推断Post 将有一个匹配的PostPolicy 类,并实例化这个类,交出当前用户和给定的记录。然后它从动作名称推断,它应该在这个策略实例上调用update?。在这种情况下,你可以想象authorize 会做这样的事情:

    unless PostPolicy.new(current_user, @post).update?
      raise Pundit::NotAuthorizedError, "not allowed to update? this #{@post.inspect}"
    end
    

    拥有所有这些,在您的情况下,如果您希望在查看所有用户列表之前授权用户,您可以定义 AdminUserPolicy 就像您已经完成的那样。然后在您的AdminUserControllerindex 操作中,

    controller do
      def index
        @users = AdminUser.all
        authorize @users // NOT `authorize current_admin_user`
        super
      end
    end
    

    如果您要检查的权限名称与操作名称不匹配,您可以将第二个参数传递给authorize。例如:

    def publish
      @post = Post.find(params[:id])
      authorize @post, :update?
      @post.publish!
      redirect_to @post
    end
    

    【讨论】:

    • 非常感谢!这清除了很多东西。像@users = Admin.all@post=Post.find() 这样的查询会增加不必要的处理,因为我的用例只知道用户是否可以使用索引、更新等方法。有没有办法将它们重定向到特定的策略?或者我可以继续使用 current_admin_user 吗?
    • 很高兴知道这一点!你能详细说明Is there a way to redirect them to specific policy?是什么意思吗
    • 我只想知道“这个用户可以查看”还是“这个用户可以编辑”。我不关心@post 变量。所以查询它们是不必要的。但是如果我忽略它们,它就不会知道要签入哪个策略。那么我该怎么说“授权:索引?”没有任何输入。
    • 您可以在authorize 方法的第一个参数中提供类而不是实例,例如authorize Post。但是你必须在第一个参数中使用一些策略对象;无论是实例、类还是符号(无头策略:github.com/elabs/pundit#headless-policies
    • 非常感谢!那会很完美。我选择了权威而不是康康康,因为我觉得它很麻烦。不过我很想知道。
    猜你喜欢
    • 1970-01-01
    • 2016-06-01
    • 2015-10-05
    • 1970-01-01
    • 2012-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多