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

Original source