简体   繁体   中英

Rails Pundit Policy Spec test failing with NoMethodError

I have just started using Pundit for authorization in my current project along with the pundit-matchers gem.

So far it seems to generally be working for me but I have a problem in my tests.

I have generally tried to follow the examples in the pundit-matchers readme and the Thunderbolt labs blog ( http://thunderboltlabs.com/blog/2013/03/27/testing-pundit-policies-with-rspec/ ).

This is my policy file;

#app/policies/procedure_policy.rb
class   ProcedurePolicy
    attr_reader :user, :procedure

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

    def index?
        user.admin?
    end

end

And this is my policy_spec file

require 'rails_helper'

describe ProcedurePolicy do
    subject {described_class.new(user, procedure)}

    let(:procedure) {FactoryGirl.create(:procedure)}

    context "for a guest" do
        let(:user) {nil}
        it {is_expected.not_to permit_action(:index)}
    end

    context "for a non-admin user" do
        let(:user) {FactoryGirl.create(:user)}
        it {is_expected.not_to permit_action(:index)}
    end

    context "for an admin user" do
        let(:user) {FactoryGirl.create(:admin_user)}
        it {is_expected.to permit_action(:index)}
    end

end

2 of my 3 tests pass; The "for a non-admin user" and "for an admin user" ones. The "for a guest" test fails with

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

Now I understand why. I'm passing nil to the #index? method of my ProcedurePolicy class which will not have an #admin? method. But all of the example specs I have found online do exactly this. What am I not seeing.

Apologies if I'm missing something really obvious. I've been away from coding for a couple of years.

Pundit calls the current_user method to set user , and typically that is provided by an authentication system like Devise or a custom solution. This means that in most scenarios, the expectation is that you always have a layer of authentication before you hit the Pundit logic, so you never have a user set to nil when it gets there.

If you want Pundit authorizations to work directly without authentication, you have to handle that in your policy definitions, ex:

class ProcedurePolicy    
  def index?
    user.present? && user.admin?
  end
end

http://www.rubydoc.info/gems/pundit#Policies

The "being a visitor" context is for testing authorisation attempts when there is no user currently authenticated in the system. Since no user is logged in, no user record/object exists - the absence of a user object is nil . This is why both the Thunderbolt Labs and pundit-matchers examples use nil to represent a visitor. The nil object does not have an admin? method, causing the error.

To correctly handle nil/guest users in your policy, check that the user object is present before checking if the user is an admin, either by checking the user object directly or using the present? method in Rails, eg

def index?
  user.present? && user.admin?
end

or just:

def index?
  user && user.admin?
end

You'll find that authentication systems such as Devise return nil from the current_user method when no user is signed in.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM