2

I'm struggling with authorization of an index action of a nested resource while using Pundit. Pundit is so slick and potentially easy, that I hate to have to cast it aside because I can't figure this out. I figure once I understand this part everything else will fall inline. I've read over a lot of people's posts asking very similar questions to the one I am asking and it seems the posts that come closest to the question I'm asking never get resolved. So, I'm still looking for an answer.

class User < ApplicationRecord
  enum role: [:user, :admin]
  after_initialization :set_default_role, :if => :new_record?
  def set_default_role
  self.role ||= :user
  has_many :galleries, dependent: :destroy
end

class Gallery < ApplicationRecord
  belongs_to :user
  validates :user_id, presence: true
  validates :title,, presence: true
end

I have user set up just how I want it with a UserPolicy. It doesn't inherit from the ApplicationPolicy because the tutorial I was looking at said I didn't need it.

my routes look like this:

devise_for :users, path: 'accounts
resources :users, shallow: true do
  resources :galleries
end

Essentially I don't want one user to see another users stuff, i.e. index, show... actions. I believe this is called a closed system. My route for seeing a user's gallery(my nested resource) index looks like this:

localhost:/users/20/galleries

I don't want User 19 to ever see what User 20 has in their gallery index. How do I do that?

This is what my GalleryController looks like:

before_action :authenticate_user! #I'm using devise
def index
  @galleries = current_user.galleries.all
  #authorize ????? <<part of what I don't understand
end

private
def gallery_params
  params.require(:gallery).permit(:title)
end

Really I don't know what I'm supposed to do in the GalleryPolicy for index. I have the show action worked out, because it's simply checking the instance of the gallery against the instance of user id. Here is the GalleryPolicy

attr_reader :user, :model

def initialize(user, model)
  @user = user
  @gallery = gallery
end

def show?
  @gallery.user_id == user.id
end

def index?
  # I can't figure it out
end

def new?
  #actually this is confusing too
end
Lenocam
  • 331
  • 2
  • 17

1 Answers1

4

First of all, I have always written Pundit policies which inherit from ApplicationPolicy so let's start from there. ApplicationPolicy is initialized with a user and a record. The record will be your model that you want the user to be authorized to view. In this case, a gallery.

Secondly, every Policy should include a scope. This is also initialized inside of ApplicationPolicy but it is your job to restrict the scope as necessary.

class GalleryPolicy < ApplicationPolicy
  class Scope < Scope
    def resolve
      scope.where(:user_id => user.id)
    end
  end

  # no need for an index? method here. we will see this in the controller

  # essentially here we are checking that the user associated to the record
  # is the same as the current user
  def show?
    record.user == user
  end

  # this is actually already the case in ApplicationPolicy but I added 
  # it here for clarity
  def new?
    create? 
  end

  # who do we want to be able to create a gallery? anyone!
  # so this simple returns true
  def create?
    true
  end
end

Then in your GalleryController you will authorize the models each time. Only the index action is a bit different as we will see now:

# controllers/gallery_controller.rb
def index 
 @galleries = policy_scope(Gallery)
 # as we specified the scope in GalleryPolicy to only include galleries which belong 
 # to the current user this is all we need here!
end

# your other actions will look something like this:
def show
  @gallery = Gallery.find(params[:gallery_id]
  authorize @gallery
end

def new
  @gallery = Gallery.new # or @gallery = current_user.galleries.build
  authorize @gallery
end

[...]

I hope this helps! Pundit is a really great tool once you get to grips with it.

cdimitroulas
  • 2,380
  • 1
  • 15
  • 22
  • I appreciate your comment. I implemented everything as you have said, but it didn't change the situation. 1st: UserA is still able to see UserB's gallery index. 2nd: Every user's gallery index has every other user's galleries on it. I want to redirect UserA to some other page with a flash message that says "Access Denied," if they try to go to UserB's gallery index page. And I don't want any other user's galleries to show up in another user's gallery index. – Lenocam Dec 09 '16 at 15:14
  • Actually, I think you;re right. The problem I'm now having is related, but not the one I asked for help solving. This works. Thanks! – Lenocam Dec 09 '16 at 19:08
  • glad I could help! If you post another question with your new problem please send me the link here and I'll have a look at it to see if I can help :) – cdimitroulas Dec 09 '16 at 23:59
  • Some additional ideas here: https://stackoverflow.com/a/33306150/1611925 – Julien Lamarche Nov 28 '21 at 03:11