diff --git a/lib/cancan/conditions_matcher.rb b/lib/cancan/conditions_matcher.rb index 009c3f6a..4234ab33 100644 --- a/lib/cancan/conditions_matcher.rb +++ b/lib/cancan/conditions_matcher.rb @@ -86,7 +86,12 @@ def matches_hash_conditions(adapter, subject, conditions) end end - def condition_match?(attribute, value) + def condition_match?(attribute, value) # rubocop:disable Metrics/MethodLength + if defined?(ActiveRecord) && + (value.is_a?(ActiveRecord::AssociationRelation) || value.is_a?(ActiveRecord::Relation)) + return condition_match_query?(attribute, value) + end + case value when Hash hash_condition_match?(attribute, value) @@ -99,6 +104,16 @@ def condition_match?(attribute, value) end end + def condition_match_query?(attribute, query) + selects = query.values[:select] + + if selects&.length != 1 + raise "Only one column should be selected and not #{selects&.length || 0} for: #{query.to_sql}" + end + + query.where(selects[0] => attribute).any? + end + def hash_condition_match?(attribute, value) if attribute.is_a?(Array) || (defined?(ActiveRecord) && attribute.is_a?(ActiveRecord::Relation)) array_like_matches_condition_hash?(attribute, value) diff --git a/spec/cancan/model_adapters/active_record_adapter_spec.rb b/spec/cancan/model_adapters/active_record_adapter_spec.rb index efb03c62..4a965709 100644 --- a/spec/cancan/model_adapters/active_record_adapter_spec.rb +++ b/spec/cancan/model_adapters/active_record_adapter_spec.rb @@ -1259,6 +1259,33 @@ class CustomPkTransaction < ActiveRecord::Base expect(CustomPkTransaction.accessible_by(ability)).to match_array([transaction1]) end + + it 'allows scope usage on can?' do + user1 = User.create! + user2 = User.create! + + ability = Ability.new(user1) + ability.can :read, Article, user_id: User.where(id: user1.id).select(:id) + + article1 = Article.create!(user: user1) + article2 = Article.create!(user: user2) + + expect(ability).to be_able_to :read, article1 + expect(ability).not_to be_able_to :read, article2 + expect(Article.accessible_by(ability)).to eq [article1] + end + + it 'raises an error if multiple columns are selected' do + user1 = User.create! + + ability = Ability.new(user1) + ability.can :read, Article, user_id: User.where(id: user1.id).select(:id, :created_at) + + article1 = Article.create!(user: user1) + + expect { ability.can?(:read, article1) } + .to raise_error(/\AOnly one column should be selected and not 2 for: SELECT/) + end end end