Skip to content

Problem using accessible_by with a belongs_to relation #905

Closed
exklamationmark opened this Issue Jul 22, 2013 · 5 comments

2 participants

@exklamationmark

Hi,

I got a problem where the call to accessible_by returns empty, while checking the permission using current_ability.can? show that I can access to the object. The details are here

Environment:

Gemfile

ruby '1.9.3'
gem 'rails', '3.2.13'
gem 'devise', '~> 2.2.4'
gem 'cancan', '~> 1.6.10'

SubscriptionAssignment model

class SubscriptionAssignment < ActiveRecord::Base
  attr_accessible :active, :child_id, :subscription_id

  belongs_to :child, :class_name => "User"
  belongs_to :subscription
  has_one :section, :through => :subscription
end

SubscriptionModel

class Subscription < ActiveRecord::Base
  attr_accessible :quota, :section_id, :user_id

  belongs_to :user
  belongs_to :section
  has_many :subscription_assignments
end

Ability

class Ability
  include CanCan::Ability

  def initialize(user)

    if user
      if user.role == 'admin'
        return admin_permission
      elsif user.role == 'parent'
        return parent_permission(user)
      elsif user.role == 'child'
        return child_permission(user)
      end
    end

    # all other casese
    return guest_permission

  end
 ...
  def parent_permission(user)
    can [:index, :show, :update, :destroy], Subscription, :user_id => user.id
    can [:create], Subscription
    can [:destroy, :show, :update], SubscriptionAssignment, subscription: {:user_id => user.id}
    can [:create], SubscriptionAssignment
  end

end

Debugging:

Subscription.all
=> [#<Subscription id: 1, user_id: 1, section_id: 110, quota: 1, created_at: "2013-07-22 02:19:58", updated_at: "2013-07-22 02:19:58">,
 #<Subscription id: 2, user_id: 4, section_id: 110, quota: 1, created_at: "2013-07-22 02:21:09", updated_at: "2013-07-22 02:21:48">]
----------------------------------------------------------------------------
SubscriptionAssignment.all
=> [#<SubscriptionAssignment id: 1, child_id: 2, subscription_id: 1, active: true, created_at: "2013-07-22 02:19:58", updated_at: "2013-07-22 02:19:58">,
 #<SubscriptionAssignment id: 2, child_id: 5, subscription_id: 2, active: true, created_at: "2013-07-22 02:21:48", updated_at: "2013-07-22 02:21:48">]
----------------------------------------------------------------------------
current_ability
=> #<Ability:0x00000006597d50
 @aliased_actions={:read=>[:index, :show], :create=>[:new], :update=>[:edit]},
 @rules=
  [#<CanCan::Rule:0x000000065979e0
    @actions=[:index, :show, :update, :destroy],
    @base_behavior=true,
    @block=nil,
    @conditions={:user_id=>1},
    @expanded_actions=[:index, :show, :update, :edit, :destroy],
    @match_all=false,
    @subjects=
     [Subscription(id: integer, user_id: integer, section_id: integer, quota: integer, created_at: datetime, updated_at: datetime)]>,
   #<CanCan::Rule:0x00000006597698
    @actions=[:create],
    @base_behavior=true,
    @block=nil,
    @conditions={},
    @expanded_actions=[:create, :new],
    @match_all=false,
    @subjects=
     [Subscription(id: integer, user_id: integer, section_id: integer, quota: integer, created_at: datetime, updated_at: datetime)]>,
   #<CanCan::Rule:0x000000065971c0
    @actions=[:destroy, :show, :update],
    @base_behavior=true,
    @block=nil,
    @conditions={:subscription=>{:user_id=>1}},
    @expanded_actions=[:destroy, :show, :update, :edit],
    @match_all=false,
    @subjects=
     [SubscriptionAssignment(id: integer, child_id: integer, subscription_id: integer, active: boolean, created_at: datetime, updated_at: datetime)]>]>
----------------------------------------------------------------------------
current_ability.can? :update, SubscriptionAssignment.find(1) => true
current_ability.can? :update, SubscriptionAssignment.find(2) => false
SubscriptionAssignment.accessible_by(current_ability) => []
SubscriptionAssignment.accessible_by(current_ability).to_sql => "SELECT \"subscription_assignments\".* FROM \"subscription_assignments\"  WHERE ('t'='f')"

I am not sure why the SQL query doesn't have an INNER JOIN. It would be great if you can give me an idea :) Thanks

@jaredbeck

Interesting problem! It turns out accessible_by takes a second optional argument, the action, which defaults to :index. See model_additions.rb.

def accessible_by(ability, action = :index)
  ability.model_adapter(self, action).database_records
end

Your current_ability for SubscriptionAssignment allows :show but not :index. None of your can rules matches, so I assume that's why the SQL contains the contradiction WHERE ('t'='f').

For future questions, please remember that github issues are for bugs or suggested features. Questions about usage are more appropriate on stackoverflow.com/tags/cancan.

@exklamationmark

Thanks, Jared!

I 'll put the question to SO then.

@jaredbeck

Ummm, you can do that if you want to .. but I already answered it, didn't I?

@exklamationmark

True, I was thinking of putting it as a self-answered question just for sharing the info.

I ended up fixing it with something like this

can [:read, :update, :destroy], SubscriptionAssignment, subscription: {:user_id => user.id}
@jaredbeck

Cool. I'm glad I could help. Please close this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.