CanCanCan’s methods are only available in a controller or view, but I want to access them in a model, more specifically in a model’s save method. The workaround I use for this is the following: I create an attr_accessor :ability in the model and assign an Ability object to it in the controller before I call save.
class Example
include ActiveModel::Model
attr_accessor :ability
def save
if ability.can?(:create, SomeOtherModel)
...
end
end
end
class ExamplesController < ApplicationController
def create
@example = Example.new(params[:example])
@example.ability = Ability.new(current_admin_user) #note: current_admin_user can be several separate models such as Admin, Support
if @example.save
...
end
end
end
Is there a better method to call can? from within a model? Are there any problems with this solution, from a security standpoint or otherwise?
You can also add an ability method in your model and delegate the can? method:
https://github.com/CanCanCommunity/cancancan/blob/develop/docs/define_check_abilities.md
With this solution, I think you will need to define
abilitymethod in all your models that need to check ability, so it can have duplicated code.From security standpoint, it can lead to unexpected things. Controllers already support strong parameters to create/update only permitted attributes of model.
For example, if Support only can update some attributes and Admin can update all attributes, how do you control that in your model?
And If you check ability in controller, it can prevent actions of user and avoid doing unnecessary queries to server.
So I think that why CanCan recommends that
canmethod works best in controllers and viewshttps://github.com/CanCanCommunity/cancancan/blob/develop/docs/controller_helpers.md