Controller helpers
As mentioned in the chapter Define and check abilities, the can? method works at its best in Rails controllers and views. This of course doesn't mean that it cannot be used everywhere.
We know already that in order to check if the user is allowed to perform a certain action we need to have a current_user method available and we can check the permission with can? :update, @article.
We can easily protect the edit and update actions of our controller by checking for the permission. Here is a very simple example:
class ArticlesController < ApplicationController
def edit
@article = Article.find(params[:id])
if can? :edit, @article
render :edit
else
head :forbidden
end
end
endauthorize!
CanCanCan provides us a authorize! helper that allows us to simplify the code above:
def edit
@article = Article.find(params[:id])
authorize! :edit, @article
render :edit
endauthorize! will raise a CanCan::AccessDenied if the action is not permitted.
You can have a global configuration on how to react to this exception in config/application.rb:
config.action_dispatch.rescue_responses.merge!('CanCan::AccessDenied' => :unauthorized)The Handling CanCan::AccessDenied Exception chapter digs deeper on how to handle the exception raised by authorize!.
:unauthorizedmight not be your favourite return status if you don't want to reveal to the user that the article exists. In such cases,:not_foundwould be a better http status.
authorize_resource, load_resource, load_and_authorize_resource
In a RESTful controller, calling authorize! action for every action can be tedious. Here we will show you, step by step, how to improve the code above.
Add authorize_resource in your controller, to call automatically authorize! action_name, @article for every action. The code above can be refactored like this:
class ArticlesController < ApplicationController
before_action :load_article
authorize_resource
def edit; end
protected
def load_article
@article = Article.find(params[:id])
end
endthe second helper method is load_resource that will perform the loading of the model automatically based on the name of the controller. The code above can be refactored like that:
class ArticlesController < ApplicationController
load_resource
authorize_resource
def edit; end
endand, clearly, load_and_authorize_resource allows to do the following:
class ArticlesController < ApplicationController
load_and_authorize_resource
def edit; end
endthis means that a completely authorized ArticlesController would look as follow:
class ArticlesController < ApplicationController
load_and_authorize_resource
def index
# @articles are already loaded...see details in later chapter
end
def show
# the @article to show is already loaded and authorized
end
def create
# the @article to create is already loaded, authorized, and params set from article_params
@article.create
end
def edit
# the @article to edit is already loaded and authorized
end
def update
# the @article to update is already loaded and authorized
@article.update(article_params)
end
def destroy
# the @article to destroy is already loaded and authorized
@article.destroy
end
protected
def article_params
params.require(:article).permit(:body)
end
endStrong parameters
You have to sanitize inputs before saving the record, in actions such as :create and :update.
For the :update action, CanCanCan will load and authorize the resource but not change it automatically, so the typical usage would be something like:
def update
if @article.update(article_params)
# hurray
else
render :edit
end
end
...
def article_params
params.require(:article).permit(:body)
endFor the :create action, CanCanCan will try to initialize a new instance with sanitized input by seeing if your controller will respond to the following methods (in order):
create_params<model_name>_paramssuch asarticle_params(this is the default convention in Rails for naming your param method)resource_params(a generic named method you could specify in each controller)
The typical usage will then be the following:
def create
if @article.save
# hurray
else
render :new
end
endIf you specify a
create_paramsorupdate_paramsmethod, CanCan will run that method depending on the action you are performing.
In the chapter dedicated to Customize controller helpers we will see more details and customizations for controllers.
There's a dedicated chapter to Nested resources.
Now that we know how Rails controllers should be protected, we can learn about the most powerful CanCanCan feature: fetching records.
