In my app User only with status == 'active'
has access to all application pages. I've create a scope policy for that action and a separate intermediate controller in which I set all global policies. As follows:
class UserPolicy < ApplicationPolicy
class Scope < Scope
def resolve
return raise(Pundit::NotAuthorizedError) unless user.status == 'active'
scope.all
end
end
end
class BaseController < ApplicationController
before_action :authorized_user
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
def authorized_user
policy_scope(current_user)
end
private
def user_not_authorized
flash[:alert] = 'You are not authorized to perform this action.'
redirect_to(request.referrer || root_path)
end
end
Now I want to have a MiniTest for this scope policy. This is what I've got:
require 'test_helper'
class UserPolicyTest < ActiveSupport::TestCase
context 'when user is active' do
def setup
@user = user(:active)
@properties = properties(:one)
end
def scope_test
# user should have access to the show page in properties controller
refute_equal properties, user
assert permit(user, properties, :show)
end
end
end
What did I missed? when I run this it shows me:
Finished in 0.185689s, 0.0000 runs/s, 0.0000 assertions/s.
0 runs, 0 assertions, 0 failures, 0 errors, 0 skips
You got it all pretty backwards. Its not the job of a scope to control overall access.
class ApplicationPolicy
class Scope
def resolve
@user.active?
scope.all
else
scope.none
end
end
end
# ...
def show?
@user.active?
end
def index?
@user.active?
end
private
def active?
user.active? # user.status == 'active' is iffy. Don't let the logic leak outside of the object
end
end
If you want to authorize the the actions of a controller you would do:
def show
authorize(Thing.find(params[:id]))
end
def index
authorize(Thing) # controlls overall access - often skipped
@things = policy_scope(Thing)
end
If you really want to add a scope to the show method you would do:
def show
authorize(policy_scope(Thing).find(params[:id]))
end
The reason your scope should NOT be controlling overall access is that is does not know the context (which action is called). So while this may "work" right now its going to blow up in your face when stuff gets more complicated.
If you then want to test a specific policy you would do:
assert ThingPolicy.new(thing, user).show?
If you want to test a scope you resolve the scope and test that the expected records are included/not included.