4

Using Rails 4.2.4 with Devise (3.5.2) and Pundit (1.0.1). Decent_exposure (2.3.2).

I have a simple nested associaton for User and Idea:

class User < ActiveRecord::Base
has_many :ideas
...

class Idea < ActiveRecord::Base
belongs_to :user
...

In routes.rb

devise_for :users

resources :users do
  resources :ideas
end

Then I am simply trying to disallow access to users/1/ideas if current_user is not the owner of the Ideas (in this example, if current_user.id != 1). I can not figure out how to do it. I am able to show just the current_user Ideas in the Index view with:

[Ideas controller]

def show
  authorize idea
end

[Idea policy]

def show?
  @current_user == @idea.user
end

But how can I prevent a user to simply navigate to other user's Idea index page? I guess that in Ideas controller I should use something like:

def index
  authorize user
end

But then what? How can I send to the User Policy the info regarding the Idea collection? Or should I authorize via the Idea Policy itself?

Galen
  • 957
  • 5
  • 16

1 Answers1

11

Duplicating my response on GitHub here because this gets more traffic.


One way is to create a stub Idea owned by the user to authorize against.

def index
  @user = User::find(params[:user_id])
  idea  = Idea.new(user_id: @user.id)

  authorize idea

  # ...
end

and an index? method in your IdeaPolicy

def index?
  record.user_id = user.id
end

Another way is to change what you're authorizing against. Instead of authorizing against Idea, authorize against the User.

def index
  @user = User::find(params[:user_id])

  authorize @user, :show_ideas?

  # ...
end

and create a new show_ideas? method on your UserPolicy

def show_ideas?
  user.id == record.id
end
deefour
  • 34,974
  • 7
  • 97
  • 90
  • It works perfectly!! Many thanks. The second approach is more intuitive for me. Thanks again! – Galen Oct 23 '15 at 18:24
  • The second approach definitely makes more sense and is easier to understand. :) – Brendon Muir Nov 20 '15 at 02:44
  • deefour, which one is the preferred way? (I'm using devise for authentication) – Sean Magyar Feb 21 '16 at 17:54
  • 6
    @SzilardMagyar the first way sits better with me. The second approach, though arguably more readable at first glance, pollutes the `UserPolicy` in my opinion. I prefer to keep policy actions as crud-like as possible: `index? new? create? edit? update? destroy?`. When I start thinking of creating actions like `can_apply_batch_action?` it usually means I'm trying to authorize against the wrong model. – deefour Feb 22 '16 at 18:01
  • @deefour Does this mean it is "okey" if I have nested routes (e.g. all models nested in `Team` model) and just do `authorize @team, :some_policy?` in every other model's controllers/views? And all the policies are stored in `team_policy.rb`? Thanks for the help! – mabu Nov 07 '17 at 15:48