cancan icon indicating copy to clipboard operation
cancan copied to clipboard

accessible_by and/or using scopes to restrict the query

Open ricktessner opened this issue 11 years ago • 4 comments

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

ricktessner avatar Mar 18 '13 22:03 ricktessner

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!

ricktessner avatar Mar 19 '13 00:03 ricktessner

Saw this issue as well... What's up with that?

stevschmid avatar May 03 '13 12:05 stevschmid

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.

jaredbeck avatar May 10 '13 04:05 jaredbeck

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!

xhoy avatar Apr 10 '14 11:04 xhoy

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).

MaksimenkoPG avatar Jul 08 '14 10:07 MaksimenkoPG