cancancan icon indicating copy to clipboard operation
cancancan copied to clipboard

Exclude with scope for index action from can(:manage, :all)

Open enthusiasmus opened this issue 6 years ago • 2 comments

Steps to reproduce

Define two rules:

      can :manage, :all

      cannot(
        %i[index retrieve],
        Model, Model.from_sandbox
      ) do ||
        m.sandbox?
      end

When accessing the index action, I receive the following error

Unable to merge an Active Record scope with other conditions. Instead use a hash or SQL for index Model ability.

from following method

    91:       def override_scope
    92:         conditions = @compressed_rules.map(&:conditions).compact
    93:         return unless conditions.any? { |c| c.is_a?(ActiveRecord::Relation) }
    94:         return conditions.first if conditions.size == 1
    95: 
    96:         raise_override_scope_error
    97:       end

Because @compressed_rules in ActiveRecord5Adapter is set as follows:

[5] pry(#<CanCan::ModelAdapters::ActiveRecord5Adapter>)> @compressed_rules
=> [#<CanCan::Rulecannot [:index, :retrieve], [Model(<attributes>)], [], {"type"=>["Model"]}>,
 #<CanCan::Rulecan [:manage], [:all], [], {}>]

the override_scope method would succeed, if only one condition exists. But the rules defined above are not merged?

Expected behavior

cannot with scope should overwrite can :manage, :all for a specific model for the index action.

Actual behavior

It raises the error mentioned above

System configuration

Rails version: 5.2.3 Ruby version: 2.6.4p104 (2019-08-28 revision 67798) [x86_64-linux] CanCanCan version 3.0.1

Thanks in advance! Best regards, Lukas

enthusiasmus avatar Nov 28 '19 12:11 enthusiasmus

It seems that overriding doesn't work with scopes at all. Is that right? Did i missed this fact in the documentation?

enthusiasmus avatar Nov 28 '19 12:11 enthusiasmus

I'm also seeing this issue.

class Book < AcrtiveRecord::Base
  scope :unpublished, -> { where(:published => false) }
end

class Ability
  include CanCan::Ability

  def initialize(user)
    can :manage, :all
    cannot :read, Book.unpublished do |book|
      book.published == false
    end
  end
end

> Book.accessible_by(Ability.new(User.new), :read).to_sql
=> "SELECT \"books\".* FROM \"books\" WHERE (TRUE=FALSE)"

... btw, I tried to create a test using your gist, but when running main.rb, it throws this error:

CanCan::NotImplemented: This model adapter does not support fetching records from the database.

gingerlime avatar Aug 11 '20 09:08 gingerlime