Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Selective permissions on STI sub-classes not respected by accessible_by #809

Open
owst opened this issue Dec 21, 2022 · 1 comment
Open

Comments

@owst
Copy link

owst commented Dec 21, 2022

Steps to reproduce

Granting read permission on a subset of STI subclass doesn't lead to relevant records being returned by accessible_by. With the following STI hierarchy:

class Vehicle < ActiveRecord::Base
class Car < Vehicle
class Motorbike < Vehicle
class Ferrari < Car
class Suzuki < Motorbike

Given can :read, Ferrari we expect Vehicle.accessible_by(ability, :index) to include any instances of Ferrari (but none of Suzuki), but instead no Vehicle records are returned.

Reproduction:

require 'bundler/inline'

gemfile(true) do
  source 'https://rubygems.org'
  gem 'rails', '= 6.1.7'
  gem 'cancancan', '= 3.4.0', require: false # require false to force rails to be required first
  gem 'sqlite3'
end

require 'active_record'
require 'cancancan'
require 'minitest/autorun'
require 'logger'

ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :vehicles, force: true do |t|
    t.string :type
  end
end

class Vehicle < ActiveRecord::Base; end
class Car < Vehicle; end
class Motorbike < Vehicle; end

class Ferrari < Car; end
class Suzuki < Motorbike; end

class Ability
  include CanCan::Ability

  def initialize
    can :read, Ferrari
  end
end

class BugTest < Minitest::Test
  def test_bug
    ferrari = Ferrari.create!
    suzuki = Suzuki.create!

    ability = Ability.new

    # These assertions pass
    assert_equal true, ability.can?(:index, Ferrari)
    assert_equal false, ability.can?(:index, Suzuki)

    # This assertion passes
    assert_equal [ferrari], Ferrari.accessible_by(ability, :index).to_a
    # This assertion also passes
    assert_equal [ferrari], Car.accessible_by(ability, :index).to_a
    # This assertion fails (the actual array is empty)
    assert_equal [ferrari], Vehicle.accessible_by(ability, :index).to_a
  end
end

This feels like an extension of #677

Expected behavior

The allowed subclass instances should be returned by klass.accessible_by for all klasses in the STI hierarchy.

Actual behavior

The allowed subclass instances are not returned by accessible_by when invoked on the base class.

System configuration

Rails version: 6.1.7

Ruby version: 2.7.6

CanCanCan version 3.4.0

@owst
Copy link
Author

owst commented Dec 21, 2022

This seems like intentional behaviour according to this suggested test: #689 (comment) My expectation also seems to be somewhat at odds with the expectation set out in another issue: #771

My expectation was that since all Ferraris and Suzukis are Vehicles (both intuitively and in the assert_equal true, ferrari.is_a?(Vehicle) sense), asking for the set of accessible Vehicles (assuming that Ferraris but not Suzukis have been marked as accessible) should include only Ferraris.

In my specific example, I would expect:

Vehicle.accessible_by(ability, :index).to_a   # => [ferrari]
Car.accessible_by(ability, :index).to_a       # => [ferrari]
Ferrari.accessible_by(ability, :index).to_a   # => [ferrari]
Motorbike.accessible_by(ability, :index).to_a # => []
Suzuki.accessible_by(ability, :index).to_a    # => []

but what we get is

Vehicle.accessible_by(ability, :index).to_a   # => []
Car.accessible_by(ability, :index).to_a       # => [ferrari]
Ferrari.accessible_by(ability, :index).to_a   # => [ferrari]
Motorbike.accessible_by(ability, :index).to_a # => []
Suzuki.accessible_by(ability, :index).to_a    # => []

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant