Reputation: 9229
I am trying to create a web app, where users have a dashboard where they can opt into a number of widgets. I have the following many-to-many:
class CreateWidgets < ActiveRecord::Migration
def change
create_table :widgets do |t|
t.string :name
t.timestamps null: false
end
create_table :users_widgets, id: false do |t|
t.belongs_to :user, index: true
t.belongs_to :widget, index: true
end
end
end
With a users model defined by the devise gem. So how can I most effectively set up some parameters which allow me to define the users ability to only see widgets they have "subscribed" to.
CanCan permissions are defined here:
class Ability
include CanCan::Ability
def initialize(user)
can :read, Widget, :user_id => user.id
end
end
Obviously, this doesn't work, as there is no parameter to track which user created the widget. So How would I set up the Widget / User models? Possibly add a list of entitlements to the user model? It could be a whole load of bool attributes, one for each type of widget, but obviously, that might get a bit messy, how should I tackle this in a security conscious way?
My models now read:
class User < ActiveRecord::Base
has_many :subscriptions
has_many :widgets, through: :subscriptions
end
class Widget < ActiveRecord::Base
has_many :subscriptions
has_many :users, through: :subscriptions
end
class Subscription < ActiveRecord::Base
belongs_to :user
belongs_to :widget
# make sure you add a unique DB index as well.
validates_uniqueness_of :user_id, scope: :widget_id
end
and my model/ability.rb
file reads:
class Ability
include CanCan::Ability
def initialize(user)
can :read, Widget do |widget|
widget.subscriptions.where(user: user).any?
end
end
end
But it still doesn't work, I expect this is due to the note about unique db index note, but I am not sure what to add for this?
Upvotes: 0
Views: 88
Reputation: 102250
You would first need to create a many to many join model between User and Widget.
class User < ActiveRecord::Base
has_many :subscriptions
has_many :widgets, through: :subscriptions
end
class Widget < ActiveRecord::Base
has_many :subscriptions
has_many :users, through: :subscriptions
end
class Subscription < ActiveRecord::Base
belongs_to :user
belongs_to :widget
# make sure you add a unique DB index as well.
validates_uniqueness_of :user_id, scope: :widget_id
end
You would then authorize by passing a block to can
:
can :read, Widget do |widget|
widget.subscriptions.where(user: user).any?
end
Upvotes: 3