简体   繁体   中英

Rails/Cancancan: Define ability with a complex/scoped query

I am building an app in Rails 4 using Cancancan for role authorization. I have an ability defined that works for individual records but not the :index action.

I have a user role called 'RVSP'. RVSPs are assigned WorkOrders , from which the RVSP will create a Record .

class WorkOrder < ActiveRecord::Base
  has_one :record
  belongs_to :rvsp, class_name: "User"
end

class Record < ActiveRecord::Base
  belongs_to :work_order
end

Here's my problem: I want RVSP users to be able to read or update Records where the WorkOrder was assigned to that RVSP. Here's the ability definition so far:

can [:read, :update], Record, Record.for_rvsp(user.id) do |record|
  work_order = record.work_order
  user.id == work_order.rvsp_id
end

The scope Record.for_rvsp is this monstrosity:

scope :for_rvsp, -> (rvsp_id) { where( work_order: { rvsp: { id: rvsp_id } } ) }

So: How do I define the ability and/or query for all Records where the Record's WorkOrder's rvsp_id matches the current user's id? I suspect it's a join of some kind but I'm new to this and haven't done that before.

PS I thought of just adding a created_by column to Record , but I already have that through PaperTrail, which I'm using for an audit trail. But Record.versions.first.whodunnit is about the same as Record.work_order.rvsp_id as far as difficulty of the query is concerned. Thanks for any advice you can offer!

PPS I'm using Postgres but I'd rather not do any database-specific SQL if I can avoid it.

I started banging on joins and it seems to be working! I replaced the scope above with the following class method:

def self.for_rvsp(rvsp_id)
  joins(:work_order).where(work_orders: {rvsp_id: rvsp_id})
end

I also tidied up the ability in Ability.rb:

can [:read, :update], Record, Record.for_rvsp(user.id) do |record|
  user.id == record.work_order.rvsp_id
end 

So, as far as index actions are concerned, it appears that you need to do the same thing in two places: you define a scope that fetches the records you need, and then you define the ability in the block. The scope ensures that you load all the right records, and the ability block authorizes them. Seems to work for me anyway!

我知道这太晚了,但是您可以遍历CanCanCan中的关联。

can [:read, :update], Record, work_order: { rsvp_id: user.id }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM