0

In my application using CanCan I have permissions where users can view and create stores but I also want them to only be able to edit the ones they've created. Users can create as many stores as they like, which all should be editable by them. A store doesn't have users so how could I do this when theirs no user_id apart of the Store table?

CanCan:

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new 
    if user.role == "default"
      can :read, Store
      can :create, Store
    end
  end
end
LearningRoR
  • 26,582
  • 22
  • 85
  • 150

3 Answers3

3

Since a user will be able to create as many stores as they like, a store will belong to a user.

You MUST create this relationship.

So, in the User model.

class User < ActiveRecord::Base
  has_many :stores
end

And in the Store model.

class Store < ActiveRecord::Base
   belongs_to :user
end

And in the ability.rb file, just put something like:

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new # guest user (not logged in)
    if user.role == 'default'
      can :manage, Store , :user_id => user.id
    end
  end
end
Kleber S.
  • 8,110
  • 6
  • 43
  • 69
  • Overriding the `initialize` method in a model is frowned upon. The correct way is to use an `after_initialize` callback. This is because ActiveRecord doesn't always call `initialize`. – Wolfram Arnold Jun 05 '12 at 22:38
1

I would add the following to the store model:

has_one :created_by, :class => User

Then add a migration to add a created_by_id to your Store class.

You should then be able to add a CanCan::Ability:

can :edit, Store, :created_by => user
Jason Noble
  • 3,756
  • 19
  • 21
  • Should I use a `has_one` if many stores can be created by a `User`? – LearningRoR May 30 '12 at 21:27
  • Yes, each Store has only one created_by user right? If you want to be able to grab a user's stores, then you should put "belongs_to :created_by, :class => User". Then you can do User.find(...).stores or Store.find(...).created_by. – Jason Noble Jun 04 '12 at 14:11
1

I agree with a previous poster, that you must set up a relationship between User and Store. The relationship can be one-to-many (as Kleber S. showed), or many-to-many, if a store can have multiple users.

Then, the best way to handle access control is in the controller, by using the association. For the show, edit, update, destroy methods, you'll need to find the store, given a logged in user, so do something like this:

class StoresController < ApplicationController
  before_filter :find_store, only: [:show, :edit, :update, :destroy]

  def show
  end

  def edit
  end

  def update
    if @store.update_attributes(params[:store])
      # redirect to show
    else
      # re-render edit, now with errors
    end
  end

  # ...

  private

  def find_store
    @store = current_user.stores.find(params[:id])
  end
end

This way, the association takes care of limiting the lookup to only those stores that are connected to the current_user by foreign key. This is the standard way for RESTful Rails resources to perform access control of associated resources.

Wolfram Arnold
  • 7,159
  • 5
  • 44
  • 64