Rahul
Rahul

Reputation: 464

Rails Cancan - How to Restrict Users From Accessing Custom URL Route

I have a custom route setup that has a location_id in the url (see below)

resources :menu_items, :path => "/location_menu/:location_id"

So when I hit /location_menu/1 it will show me location_1's menu, /location_menu/2 will show location_2's menu, etc.

Each user is associated to multiple locations (has_many :locations)

I am trying to use cancan to restrict users from viewing certain menu_item URLS.

For example: User 1 is associated with location 1 and 2. So they can only view the page /location_menu/1 and /location_menu/2. But they would not be able to view /location_menu/3.

I created a custom method as a before_filter in my controller:

before_filter :location_check
 ... 
def location_check
  @location = Location.find(params[:location_id])
  authorize! :see_location, @location
end

In my ability.rb

can :see_location, MenuItem do |location| location && user.location_ids.include?(location.id) end

For some reason, this does not work for me. What could I be doing wrong? If you guys could help me, I would really appreciate it!

Thanks.

Upvotes: 1

Views: 2074

Answers (2)

Ganesh Kunwar
Ganesh Kunwar

Reputation: 2653

Check once within ability.rb,

user.location_ids.each do |l|
  can :view, MenuItem, location_id: l
end

Upvotes: 1

Zach Kemp
Zach Kemp

Reputation: 11904

First, I'm not seeing what benefit you get from defining a custom route rather than either a nested resource, but it looks likes its inflating your domain language and resulting in a situation where any particular resource could be referred to by several different names - this will probably become confusing enough that it's worth it to simplify now.

In your location_check method, you are authorizing see_location on a Location instance, but the portion of the Ability class you've shown us concerns a MenuItem class. Try defining your ability like this:

can :see_location, Location, id: user.location_ids

Edit

If you need a cancan action to authorize MenuItems directly, try this (assuming a belongs_to :location relationship on MenuItem):

can :view, MenuItem, location_id: user.location_ids

As for your routes, think even simpler. All you need is this:

resources :location_menus

...and then everything related to this goes in a LocationMenusController. Don't worry that there's no model by the same name - you still can still lookup your locations with Location.find(params[:id]). From what I understand, everything about the location menu pages hinges around the current user's access to a particular location, so you can treat LocationMenu as a kind of virtual resource wrapped around Location.

Upvotes: 0

Related Questions