CodeKitchen

RailsでのcancancanによるAuthorization入門

rails ruby cancancan

1. はじめに

Web アプリケーションを開発する際、認証 (Authentication) と認可 (Authorization) は非常に重要な概念です。認証は、ユーザーが誰であるかを確認するプロセスであるのに対し、認可は、ユーザーが特定のリソースやアクションにアクセスする権限を持っているかどうかを決定するプロセスです。

Ruby on Rails には、認可を処理するためのさまざまな gem があります。その中でも、cancancan は、シンプルで柔軟性の高い認可ソリューションとして広く使用されています。cancancan は、アプリケーションのリソースへのアクセスを制御し、ユーザーのロールとパーミッションを管理することができます。

cancancan の主な利点は以下のとおりです。

  1. シンプルで直感的な DSL (ドメイン固有言語) を使用してアクセス制御ルールを定義できる
  2. コントローラ、ビュー、モデルなどさまざまな場所で認可チェックを行える
  3. ロールとパーミッションの管理が容易
  4. 継承によってルールを柔軟に拡張できる
  5. Rails アプリケーションへのシームレスな統合

この記事では、cancancan の基本的な使い方から、より高度なユースケースまで、実際のコード例を交えて解説していきます。これを読むことで、Rails アプリケーションにおける認可の重要性を理解し、cancancan を使って効果的にアクセス制御を実装できるようになるでしょう。

2. cancancanのインストールと設定

最初に、cancancanをRailsアプリケーションにインストールし、設定する方法を説明します。

2.1 Gemfileへの追加

まず、Gemfilecancancanを追加します。

gem 'cancancan'

その後、bundle installを実行してgemをインストールします。

bundle install

2.2 Abilityクラスの生成

cancancanは、Abilityクラスを使用してアクセス制御ルールを定義します。Abilityクラスを生成するには、以下のコマンドを実行します。

rails g cancan:ability

このコマンドにより、app/models/ability.rbファイルが生成されます。

2.3 Abilityクラスの設定

生成されたAbilityクラスは、以下のようなコードから始まります。

# app/models/ability.rb
class Ability
  include CanCan::Ability

  def initialize(user)
    # Define abilities for the user here. For example:
    #
    #   return unless user.present?
    #   can :read, :all
    #   return unless user.admin?
    #   can :manage, :all
    #
    # The first argument to `can` is the action you are giving the user
    # permission to do.
    # If you pass :manage it will apply to every action. Other common actions
    # here are :read, :create, :update and :destroy.
    #
    # The second argument is the resource the user can perform the action on.
    # If you pass :all it will apply to every resource. Otherwise pass a Ruby
    # class of the resource.
    #
    # The third argument is an optional hash of conditions to further filter the
    # objects.
    # For example, here the user can only update published articles.
    #
    #   can :update, Article, published: true
    #
    # See the wiki for details:
    # https://github.com/CanCanCommunity/cancancan/blob/develop/docs/define_check_abilities.md
  end
end

Abilityクラスのinitializeメソッドは、現在のユーザーを引数として受け取ります。ここで、canメソッドを使用して、ユーザーのアクセス制御ルールを定義します。

次の章では、実際にリソースへのアクセス制御ルールを定義する方法について説明します。

3. リソースへのアクセス制御

cancancanでは、canメソッドを使用して、ユーザーがリソースに対して実行できるアクションを定義します。一般的なアクションには、:read:create:update:destroyなどがあります。

3.1 アクションの定義

以下は、Abilityクラスでアクションを定義する例です。

# app/models/ability.rb
class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new # ゲストユーザー(ログインしていないユーザー)の場合

    if user.admin?
      can :manage, :all # 管理者は全てのアクションを実行可能
    else
      can :read, :all # 全てのユーザーが全てのリソースを読み取り可能
      can :create, Article # 全てのユーザーが記事を作成可能
      can :update, Article, user_id: user.id # ユーザーは自分の記事を更新可能
      can :destroy, Article, user_id: user.id # ユーザーは自分の記事を削除可能
    end
  end
end

この例では、管理者は全てのアクションを実行でき、一般ユーザーは全てのリソースを読み取ることができます。また、一般ユーザーは記事を作成でき、自分の記事を更新・削除できます。

3.2 Abilityクラスでのアクセス制御ルールの定義

canメソッドの第一引数はアクション、第二引数はリソース、第三引数は条件をハッシュで指定します。条件を指定することで、より細かなアクセス制御が可能になります。

以下は、条件を指定する例です。

can :update, Article, published: true

この例では、公開済みの記事のみ更新可能であることを示しています。

4. コントローラでの認可チェック

コントローラで認可チェックを行うには、authorize_resourceメソッドを使用します。これにより、アクションが実行される前に、ユーザーがそのアクションを実行する権限を持っているかどうかがチェックされます。

4.1 authorize_resourceメソッドの使用

以下は、ArticlesControllerauthorize_resourceを使用する例です。

# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  load_and_authorize_resource

  def index
    # @articles = Article.all
  end

  def show
    # @article = Article.find(params[:id])
  end

  def new
    # @article = Article.new
  end

  def create
    # @article = Article.new(article_params)
    if @article.save
      redirect_to @article
    else
      render :new
    end
  end

  # 他のアクション(edit、update、destroyなど)も同様
end

load_and_authorize_resourceメソッドを使用することで、各アクションの前に自動的に認可チェックが行われます。また、@articleなどのインスタンス変数も自動的にセットされるため、コメントアウトしている部分のコードは不要になります。

次の章では、ビューでの認可チェックについて説明します。

5. ビューでの認可チェック

ビューでは、can?メソッドを使用して、現在のユーザーがリソースに対して特定のアクションを実行できるかどうかを確認できます。これにより、ユーザーのパーミッションに基づいて、UIの要素を表示したり非表示にしたりすることができます。

5.1 can?メソッドの使用

以下は、articles/index.html.erbcan?メソッドを使用する例です。

<h1>記事一覧</h1>

<% if can? :create, Article %> <%= link_to '新規記事作成', new_article_path %>
<% end %>

<table>
  <thead>
    <tr>
      <th>タイトル</th>
      <th>アクション</th>
    </tr>
  </thead>
  <tbody>
    <% @articles.each do |article| %>
    <tr>
      <td><%= link_to article.title, article_path(article) %></td>
      <td>
        <% if can? :update, article %> <%= link_to '編集',
        edit_article_path(article) %> <% end %> <% if can? :destroy, article %>
        <%= link_to '削除', article_path(article), method: :delete, data: {
        confirm: '本当に削除しますか?' } %> <% end %>
      </td>
    </tr>
    <% end %>
  </tbody>
</table>

この例では、ユーザーが記事を作成できる場合は「新規記事作成」リンクが表示され、記事を更新・削除できる場合は、各記事の横に「編集」「削除」リンクが表示されます。

6. ロールとパーミッションの管理

cancancanを使用すると、ロールとパーミッションを簡単に管理できます。ロールは、ユーザーのグループ(管理者、編集者、一般ユーザーなど)を表し、パーミッションは、各ロールがリソースに対して実行できるアクションを定義します。

6.1 ロールの定義

まず、Userモデルにロールを表すカラムを追加します。この例では、roleという名前のカラムを追加し、列挙型を使用してロールを定義します。

# app/models/user.rb
class User < ApplicationRecord
  enum role: { user: 0, editor: 1, admin: 2 }
end

6.2 ユーザとロールの関連付け

次に、Abilityクラスでロールに基づいたアクセス制御ルールを定義します。

# app/models/ability.rb
class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new # ゲストユーザー(ログインしていないユーザー)の場合

    if user.admin?
      can :manage, :all
    elsif user.editor?
      can :read, :all
      can :manage, Article
    else
      can :read, :all
    end
  end
end

この例では、管理者は全てのアクションを実行でき、編集者は全てのリソースを読み取り、記事を管理(作成、更新、削除)できます。一般ユーザーは全てのリソースを読み取ることができます。

以上で、ロールとパーミッションの管理方法について説明しました。次の章では、cancancanの動作の流れを理解するために、シーケンス図を使って説明します。

7. cancancanの動作の理解

cancancanの動作を理解するために、リクエストからレスポンスまでの流れをMermaidを使ったシーケンス図で説明します。

ViewModelAbilityControllerUserViewModelAbilityControllerUseralt[認可成功][認可失敗]リクエスト認可チェック認可結果データ取得・操作データデータ渡すレンダリング結果レスポンス403 Forbidden
  1. ユーザーがコントローラにリクエストを送信します。
  2. コントローラは、authorize_resourceまたはload_and_authorize_resourceメソッドを使用して、Abilityクラスに認可チェックを依頼します。
  3. Abilityクラスは、定義されたルールに基づいて認可の可否を判定し、結果をコントローラに返します。
  4. 認可が成功した場合、コントローラはモデルからデータを取得・操作します。
  5. モデルはデータをコントローラに返します。
  6. コントローラはデータをビューに渡します。
  7. ビューはデータを元にレンダリングを行い、結果をコントローラに返します。
  8. コントローラはレスポンスをユーザーに返します。
  9. 認可が失敗した場合、コントローラは403 Forbiddenのステータスコードをユーザーに返します。

この流れにより、cancancanを使用したアクセス制御が適切に機能していることがわかります。

8. よくある問題と解決策

cancancanを使用する際に、よくある問題とその解決策について説明します。

8.1 認可エラーのデバッグ

認可エラーが発生した場合、以下の点を確認してください。

  1. Abilityクラスで適切なルールが定義されているか
  2. コントローラでauthorize_resourceまたはload_and_authorize_resourceメソッドが呼び出されているか
  3. ビューでcan?メソッドが適切に使用されているか

また、エラーメッセージを確認することで、問題の原因を特定しやすくなります。

8.2 パフォーマンスの最適化

大規模なアプリケーションでは、認可チェックがパフォーマンスに影響を与える可能性があります。以下のような最適化を行うことで、パフォーマンスを改善できます。

  1. イーガーローディングを使用して、必要なデータを事前にロードする
  2. インデックスを適切に設定して、クエリのパフォーマンスを向上させる
  3. キャッシュを活用して、繰り返し実行されるクエリの結果を再利用する

また、Abilityクラスでのルール定義を最適化することで、不要な処理を減らすこともできます。

9. まとめ

この記事では、Railsアプリケーションでcancancanを使用してAuthorization(認可)を実装する方法について、初心者向けに説明しました。以下の内容を学びました。

  1. cancancanのインストールと設定方法
  2. リソースへのアクセス制御ルールの定義方法
  3. コントローラとビューでの認可チェックの実装方法
  4. ロールとパーミッションの管理方法
  5. cancancanの動作の理解
  6. よくある問題と解決策

cancancanを使用することで、アプリケーションのセキュリティを向上させ、ユーザーのアクセス制御を柔軟に管理できます。この記事で学んだ内容を活かして、より安全で使いやすいRailsアプリケーションを開発してください。

今後の学習には、以下のリソースが役立ちます。

logo

Web Developer。パフォーマンス改善、データ分析基盤、生成AIに興味があり。Next.js, Terraform, AWS, Rails, Pythonを中心に開発スキルを磨いています。技術に関して幅広く投稿していきます。