Reputation: 9773
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.
Upvotes: 1
Views: 1486
Reputation: 3035
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, e.g.
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.
Upvotes: 1
Reputation: 4713
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
Upvotes: 2