Jesse Sravya
Jesse Sravya

Reputation: 121

NoMethodError undefined method `admin?' for nil:NilClass Pundit, Devise Rails

I'm trying to integrate pundit with my active admin and devise configuration. But the app works weirdly. It takes in model/record as current_user.

my policy file:

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:

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

The log is as follows:

--------------- 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)

The weird part is, according to logs, current_user=nil and model=#<AdminUser:0x007f27733f8a80>

I swapped current_user with model my policy file as

def index?
  @record.admin?
end

And it works! I don't understand this strange behaviour.

Upvotes: 1

Views: 976

Answers (1)

Wasif Hossain
Wasif Hossain

Reputation: 3950

Pundit policy doc says that it calls the current_user method to retrieve what to send into the first argument of the initialize method inside the Policy class. If you have configured ActiveAdmin to retrieve the current logged in user by using current_admin_user, then you have to override the pundit default method in your ApplicationController class like so: Ref

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

In order to make the defined policy working, you have to invoke authorize inside the controller action with the instance of the corresponding policy model. So if you have a PostPolicy and you want to authorize the update action, you have to do the following:

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

The authorize method automatically infers that Post will have a matching PostPolicy class, and instantiates this class, handing in the current user and the given record. It then infers from the action name, that it should call update? on this instance of the policy. In this case, you can imagine that authorize would have done something like this:

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

Having all these, in your case, if you want that a user should be authorized before viewing the list of all users, you can define the AdminUserPolicy like you have done already. Then in the index action of your AdminUserController,

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

You can pass a second argument to authorize if the name of the permission you want to check doesn't match the action name. For example:

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

Upvotes: 1

Related Questions