How to use pundit scopes?
authorization, pundit, ruby-on-rails-4
Solution
Reading through this from a security perspective I can see a couple things that bear mentioning. For example, if you are allowing users to specify the `student_id` and `instructor_id` param fields, what's to stop them from passing in an ID for someone other than themselves? You don't ever want to let a user specify who they are, especially when you are basing policies on the users type.
For starters, I would implement Devise and add an additional boolean field called `instructor` that would be `true` when the user was an instructor but default to `false` for students.
Then your `User`s would automatically have an `instructor?` method defined, which will return `true` if the value in the `instructor` column is `true`.
You could then add a helper for students:
def student?
!instructor?
end
Now using Devise (which gives us access to a `current_user` variable) we can do things like `current_user.instructor?` which will return `true` if they are an instructor.
Now on to the policy itself. I just started using Pundit a few weeks ago, but this is what I'd do in your situation:
class GoalPolicy < ApplicationPolicy
class Scope < GoalPolicy
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
@scope.where(user: @user)
end
end
end
Then your (I'm assuming `GoalsController` class and `index` method) method can look like:
def index
policy_scope(Goal) # To answer your question, Goal is the scope
end
If you wanted to order you could also do
def index
policy_scope(Goal).order(:created_at)
end
I just realized that you asked this question half a year ago, but hey! Maybe it'll answer some questions other people have and maybe I'll get some feedback on my own budding Pundit skills.
Problem
I have just made the switch to Pundit from CanCan. I am unsure about a couple of things, and how Pundit is best used. For example: If you have a resource that can have multiple parent objects, for instance lets say a Goal belongs to a student and instructor. Therefor, a student can have many goals and an instructor can have many goals. In a controller index action you might do: ``` if params[:student_id].present? @account = Student.find(params[:student_id]) @goals = @account.goals elsif params[:instructor_id].present? @account Instructor.find(params[:instructor_id]) @goals = @account.goals end ``` `params` are not usable inside policies, so the logic needs to be done here. I think. For what I can tell, if you skip the `policy_scope` you will get an unauthorized error when viewing the index page for goals. Would you: ``` @goals = policy_scope(@account.goals) ``` OR ``` @goals = policy_scope(Goal.scoped).where( account_id: @account.id) ``` What happens when you throw a bunch of includes in the mix? ``` @example = policy_scoped(@school.courses.includes(:account => :user, :teacher )) ``` Or when needed to order...is this correct? ``` policy_scope(Issue.scoped).order("created_at desc") ``` When using scopes: What is `:scope` here? Is `:scope` an instance of the model being evaluated? I've tried accessing its attributes via `:scope`, but didn't work. ``` class Scope < Struct.new(:user, :scope) ```