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
end
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
end
authorize!
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!
.
:unauthorized
might not be your favourite return status if you don't want to reveal to the user that the article exists. In such cases,:not_found
would be a better http status.
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
end
the 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
end
and, clearly, load_and_authorize_resource
allows to do the following:
class ArticlesController < ApplicationController
load_and_authorize_resource
def edit; end
end
this 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
end
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)
end
For 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>_params
such 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
end
If you specify a
create_params
orupdate_params
method, 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.