Reputation: 2198
I need to fetch only specific records from the database on a certain condition using CanCanCan. This is my data model.
class User < ApplicationRecord
has_many :regions_users
end
class RegionsUser < ApplicationRecord
belongs_to :region
end
class Region < ApplicationRecord
has_many :regions_users
has_many :sites
end
class Site < ApplicationRecord
belongs_to :region
has_many :offers
end
class Offer < ApplicationRecord
belongs_to :site
end
For example, a user is assigned to two regions, which means that the user
user.regions_users
returns and array of two RegionsUser [RegionsUser1, RegionsUser2]. RegionsUser1 belongs to a Region A and RegionsUser2 belongs to Region B so if I am logged in as the user I am a region user that can oversee the data belonging to those regions. Now, I need to display Offers that belong to those regions (Region A and Region B). That means, that the user cannot access an offer belonging to a region other than Region A and Region B, so accessing /offers/3
should raise Access Denied
error.
I can pull the regions from RegionsUser:
region_ids = user.regions_users.pluck(:regions_id)
and then the Offers:
Offer.joins(:site).where(sites: { region_id: region_ids })
I read about defining abilities using a block described here
can :update, Project, ["priority < ?", 3] do |project|
project.priority < 3
end
but I can't think of any solution for my case. I would appreciate any ideas.
UPDATE
This kinda works, but instead of displaying Access Denied page it raises 404 Page Not Found error.
offers_controller.rb
def offer
@offer ||= Offer.accessible_by(current_ability).find(params[:id])
end
ability.rb
if user.region_user?
region_ids = user.regions_users.pluck(:region_id)
can :read, Offer, site: { region_id: region_ids }
end
UPDATE 2
I could catch ActiveRecord::RecordNotFound
and raise CanCanCan::AccessDenied
but that is a workaround. I would expect CanCanCan to handle the authorization part. I could just pull the records in the controller and raise an expcetion but that doesn't really relate to CanCanCan, does it?
Upvotes: 1
Views: 572
Reputation: 1722
I think you need load_and_authorize_resource
in your controllers. I don't think it will raise the
raise CanCan::AccessDenied.new("Not authorized!", :read, Article)
unless you do that.
The accessible_by(currrent_ability)
simply scoped the result set to those defined in your ability file
Upvotes: 1