简体   繁体   中英

Allowing access cancan / cancancan when id is not nil

I'm trying to give access where an association exists using cancan.

It needs to work with both object attributes and accessible_by selection (so I can't use a block, unless someone tells me how I get around the error when I do that).

I've come up with a brutal hack.

lead.rb
has_many :recordings
has_many :emails

ability.rb

can :manage, Lead
can :manage, Recording, :lead_id => (1..Lead.pluck(:id).max).to_a
can :manage, Email, :lead_id => (1..Lead.pluck(:id).max).to_a

What I mean is lead_id is not null...

Is there any way I can do that without creating a 100,000 item array every time?


Extra info: It's worth noting that these are not the only controls in place on the Recording and Emails models, so it's important that I can add the extra permissions rather than reset and express them negatively.

There are two ways to achieve this:

1. A combined hash of abilities

This is the recommended approach . Use this, unless you have a good reason not to.

The idea here is to combine a cannot ability, in conjunction with can :

can :manage, Recording
cannot :manage, Recording, lead_id: nil

Note that the order is important here, so that the rule overriding is correct.

2. A block of abilities

By defining the ability with a block , you can form more complicated queries.

A simple implementation would be as follows:

can :manage, Recording do |recording|
  !recording.lead_id.nil?
end

However, in order to combine this ability with others, you must also specify the SQL conditions when fetching records. This additional SQL controls load_resource actions, such as index :

can :manage, Recording, ["lead_id IS NOT NULL"] do |recording|
  !recording.lead_id.nil?
end

In order to keep this logic DRY, you could also consider defining your permissions in a block such as:

[Recording, Email].each do |model|
  can :manage, model
  cannot :manage, model, lead_id: nil
end

I had this need, and here's a solution that is probably much faster. You don't have to hit the database with a pluck , and there's no reason to change a range to an array, as checking the range works fine (I'm not sure if this uses a range internally in the library, but ideally it would).

can :manage, [Recording, Email], :lead_id => (1..(2**31-1))

I'm using cancancan .

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