CanCanCommunity/cancancan

View on GitHub
docs/controller_helpers.md

Summary

Maintainability
Test Coverage
# Controller helpers

As mentioned in the chapter [Define and check abilities](./define_check_abilities.md), 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:

```ruby
class ArticlesController < ApplicationController
  def edit
    @article = Article.find(params[:id])
    if can? :edit, @article
      render :edit
    else
      head :forbidden
    end
  end
end
```

## authorize!

CanCanCan provides us a `authorize!` helper that allows us to simplify the code above:

```ruby
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`:

```ruby
config.action_dispatch.rescue_responses.merge!('CanCan::AccessDenied' => :unauthorized)
```

The [Handling CanCan::AccessDenied Exception](./handling_access_denied.md) 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.

## 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:

```ruby
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:

```ruby
class ArticlesController < ApplicationController
  load_resource
  authorize_resource

  def edit;  end
end
```

and, clearly, `load_and_authorize_resource` allows to do the following:

```ruby
class ArticlesController < ApplicationController
  load_and_authorize_resource

  def edit; end
end
```

this means that a completely authorized `ArticlesController` would look as follow:

```ruby
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
```

## Strong 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:

```ruby
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):

1. `create_params`
2. `<model_name>_params` such as `article_params` (this is the default convention in Rails for naming your param method)
3. `resource_params` (a generic named method you could specify in each controller)

The typical usage will then be the following:

```ruby
def create
  if @article.save
    # hurray
  else
    render :new
  end
end
```

> If you specify a `create_params` or `update_params` method, CanCan will run that method depending on the action you are performing.

In the chapter dedicated to [Customize controller helpers](./changing_defaults.md) we will see more details and customizations for controllers.

There's a dedicated chapter to [Nested resources](./nested_resources.md).

Now that we know how Rails controllers should be protected, we can learn about the most powerful CanCanCan feature: [fetching records](./fetching_records.md).