Pundit: Authorizing Index views with nested resources?

Created on 23 Oct 2015  路  3Comments  路  Source: varvet/pundit

I can not figure out how to authorize the access to an Index view based on the parent resource.

User has_many :ideas
Idea belongs_to :user

resources :users do
resources :ideas
end

Then I do not want a user to navigate to users/1/ideas if the user is not the one with id=1. Other words, if the user is not the ideas owner.

Any help would be very appreciated,
Best

Most helpful comment

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

All 3 comments

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

Exactly. It works perfectly. I prefer the second approach, more intuitive for me...

I had the same issue with the index action and solved it in the controller. My authorization requires a User to belong to an Organization, and only an Admin who belongs to the same Organization as the User can view the Ideas.

def index
  authorize(Idea)
  user = User.find_by_uuid(params[:user_id])

  # Check the Idea's parent resource outside of the Policy - as the Policy
  # only operates on the resource itself (i.e. the Idea).

  # @current_user is an Admin
  unless user.organization_id == @current_user.organization_id
    raise Pundit::NotAuthorizedError
  end

  # Apply the organization_id scope to be on the safe side.
  ideas = policy_scope(user.ideas)
  render(json: ideas, status: 200)
end
Was this page helpful?
0 / 5 - 0 ratings