accessible_by and/or using scopes to restrict the query #840

Open
ricktessner opened this Issue Mar 18, 2013 · 4 comments

Comments

Projects
None yet
5 participants

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.

xhoy commented Apr 10, 2014

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment