Pundit- Index Method for Admin and Users
Asked Answered
H

1

7

So, I'm trying to use the gem pundit. I'm just trying to figure out how to have an index view for users and admins. I want to render all results for an admin and only related posts for a user. I've googled and searched on github, but I'm not find any luck. What do I have to put in my policy and controller?

original code

class PostsPolicy
   attr_reader :current_user, :model

  def initialize(current_user, model)
    @current_user = current_user
    @post = model
  end

  def index?
    @current_user.admin?
  end
end

controller

class PostsController < ApplicationController
  before_filter :load_user
  before_filter :authenticate_user!
  after_action :verify_authorized

  def index
    @posts = Post.order('title').page(params[:page]).per(25)
    authorize User
  end

private

  def load_user
    @user = User.find_by_id(params[:user_id])
  end

end

second update

class PostsPolicy
 class Scope
  attr_reader :user, :scope

  def initialize(user, scope)
     @user = user
     @scope = scope
  end

  def resolve
    if user.admin?
      scope.all
    else
      scope.where(user: user)
    end
   end

  end

end

controller

class PostsController < ApplicationController
  before_filter :load_user
  before_filter :authenticate_user!
  after_action :verify_authorized

 def index
   @posts = policy_scope(Post).order('title').page(params[:page]).per(25)
   authorize User
 end

private

 def load_user
   @user = User.find_by_id(params[:user_id])
 end
end

third update

class PostPolicy

   class Scope
    attr_reader :user, :scope

      def initialize(user, scope)
         @user = user
         @scope = scope
      end

      def resolve
        if user.admin?
          scope.all
        else
          scope.where(user: user)
        end
      end
   end

  def index?
   user.admin? || user.posts.count > 0
  end
end

controller

class PostsController < ApplicationController
  before_filter :load_user
  before_filter :authenticate_user!
  after_action :verify_authorized

 def index
   @posts = policy_scope(Post).order('title').page(params[:page]).per(25)
   authorize User
 end

private

 def load_user
   @user = User.find_by_id(params[:user_id])
 end

end

** final update with working code **

class PostPolicy
attr_reader :user, :model

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

   class Scope
    attr_reader :user, :scope

      def initialize(user, scope)
         @user = user
         @scope = scope
      end

      def resolve
        if user.admin?
          scope.all
        else
          scope.where(user: user)
        end
      end
   end

  def index?
   user.admin? || user.posts.count
  end
end

controller

class PostsController < ApplicationController
  before_filter :load_user
  before_filter :authenticate_user!
  after_action :verify_authorized

 def index
   @posts = policy_scope(Post).order('title').page(params[:page]).per(25)
   authorize Post
 end

private

 def load_user
   @user = User.find_by_id(params[:user_id])
 end

end
Hagood answered 11/12, 2014 at 2:58 Comment(0)
O
7

What you're looking for is a Scope:

class PostsPolicy
  class Scope
    attr_reader :user, :scope

    def initialize(user, scope)
       @user = user
       @scope = scope
    end

    def resolve
      if user.admin?
        scope.all
      else
        scope.where(user: user)
      end
    end
  end
end

Then in your controller

def index
  @posts = policy_scope(Post).order('title').page(params[:page]).per(25)
  authorize User
end

Edit

As a side note, authorize User will probably not serve you well in the long run. You're essentially creating an index policy that would need to serve every index. If you want to to authorize visibility to the index page you can still do something like this in your policy:

def index?
  user.admin? || user.posts.count > 0
end

Assuming that relationship is set up, then you would call authorize Post in your index controller before your policy_scope.

Oneidaoneil answered 11/12, 2014 at 3:12 Comment(13)
Thank you! I have the following error for users (not an admin) trying to access the index Pundit::NotAuthorizedError - not allowed to index? this User: pundit (0.3.0) lib/pundit.rb:74:in`` authorize' app/controllers/posts_controller.rb:15:in index'`Hagood
Awesome! Progress! I'm assuming this user is either not an admin and/or doesn't have any posts?Oneidaoneil
Admin- no, Has posts- yes. Should I change my scope to allow users to see their own post and admins see all? (silly question, but I thought it was already that way?)Hagood
Let's take a step back for a second: First, this error occurs because Pundit gives you the freedom to do whatever you want with a failed authorization. This will catch the error and redirect to wherever you want it to go: github.com/elabs/… The only thing your scope is going to do is whittle down the collection. It won't actually prevent someone from going to the page. The thing that is preventing you from accessing the page is the index? method on your user policy. I'm guessing it's something like user.admin?Oneidaoneil
Does this all make sense so far?Oneidaoneil
Yes, thank you. I see what you are saying. That said, if I want the users to access their own posts, would you recommend I make the as a separate bounce back method? Or should I make an if statement checking the role and then providing an index of the matching post if they are not an admin. Thanks again in advance for your assistance.Hagood
If there's no harm in letting a user see the index page with or without posts I don't see any reason to have an authorize in there at all. If you don't want the empty screen, you can do the authorize on Post like I suggest in my edit.Oneidaoneil
So close yet so far. I have updated my question with the latest code that includes your suggestions. I'm hitting an authorize error that I assumed would have disappeared with the latest suggestion that took the admin or the user, however, no luck. Any other suggestions or maybe I'm missing a letter or such.Hagood
Are you calling authorize Post as suggested? You say you updated your question but the question still calls authorize User. The authorize method determines which policy to load based on the argument passed, so authorize User is going to look for a user policy, something you'd want to do in the UsersController. You want to authorize Post, which would load your post policy. The policy methods always have access to the current_user as user by default.Wickiup
Thank you very much for the clarification. That does shine some light on my confusion. I have updated my index controller method, but I have hitting an wrong number of arguments (2 for 0) when I apply the updateHagood
I figured it out, I needed to have a separate initialize method. I have updated my code. Thank you all for your help.Hagood
@Hagood it's too late for this question, but in the future, you should refrain from updating your question to incorporate fixes. The point of SO after all is not to help only one person, but for the question and answer to stick around that others might be helped as well. By fixing your code in the question you've made it so no one else can ever be helped by the exchange here, and will actually likely be confused by a question with working code.Wickiup
great, thanks for the tip, I will update the question to represent the transition.Hagood

© 2022 - 2024 — McMap. All rights reserved.