cancan
cancan copied to clipboard
accessible_by and/or using scopes to restrict the query
Hi,
I have a model with a self.accessible_by(ability, action) method defined. Let's call the model Thing. This method does return an ActiveRecord::Relation instance.
In the ThingController, load_and_authorize_resource is used and the index() action returns the correct records as restricted by the accessible_by() method. Great.
However, if I go the the show() action in the controller, I can retrieve any Thing I choose, even those that should be restricted by accessible_by().
I've checked, and sure enough, accessible_by() is not being used when individual records are being retrieved.
Is there a way to get the load_and_authorize_resource to always make use of the accessible_by() method?
I've tried defining access as:
can :read, Thing, Thing.accessible_by(self)
but that fails horribly when trying to fetch the collection with the "The can? and cannot? call cannot be used with a raw sql 'can' definition. The checking code cannot be determined for :index Thing(...)"
Even trying
can :read, Thing, Thing.scoped
fails. I'm not at all clear on how one would even use a scope on defining an ability. From the docs, I'm left with the impression that the above (defining the 3rd parameter) should work just fine.
I think I'm missing something really really simple here.
Thanks for your help,
Rick
I thought I'd just provide some simple code to show what I'm using
The Ability class
class Ability
include CanCan::Ability
def initialize(user)
can :manage, Thing
end
end
The Thing model
class Thing < ActiveRecord::Base
attr_accessible :name
def self.accessible_by(ability, action)
# Instead of a single simple SQL 'WHERE' clause, imagine this accessible_by method can
# return a variety of different conditions depending on roles the user has.
where('id < 10')
end
end
Finally, the index and show actions of the ThingController
class ThingsController < ApplicationController
load_and_authorize_resource
def index
respond_to do |format|
format.html # index.html.erb
format.json { render json: @things }
end
end
def show
respond_to do |format|
format.html # show.html.erb
format.json { render json: @thing }
end
end
end
The index action will only grab the Thing's with an id of less than 10, as expected. However, I can show() any Thing item regardless of the id value. I would have expected that the accessible_by
scope would be used in loading the Thing resource as well.
I did try changing the can
to
can :manage, Thing, Thing.accessible_by(self, :index)
but got the error mentioned in the original post. Even using Thing.scoped
in that can
definition resulted in that same error.
I'm wondering what the proper way is to get the show() etc methods restricted by a scope.
I hope this clarifies what I'm trying to accomplish. Thanks!
Saw this issue as well... What's up with that?
First, I'm pretty sure you're not supposed to override accessible_by
. Second, you are using the "hash of conditions" syntax for can
, when I think you should be using the block syntax.
Dear submitter, Since cancan/raynB hasn't been active for more than 6 months and no body else then ryam himself has commit permissions the cancan project is on a stand still. Since cancan has several issues including missing support for rails 4 cancan is moving forward to cancancan. More details on: #994
If your feel that your pull request or bug is still applicable (and hasn't been merged in to cancan) it would be really appreciated if you would resubmit it to cancancan (https://github.com/cancancommunity/cancancan)
We hope to see you on the other side!
A block's conditions are only executable through Ruby. If you are Fetching Records using accessible_by it will raise an exception. To fetch records from the database you need to supply an SQL string representing the condition. The SQL will go in the WHERE clause, if you need to do joins consider using sub-queries or scopes (below).