PunditとCanCanCanの比較 | Dev Driven 開発・デザインチーム PunditとCanCanCanの比較 – 働くひとと組織の健康を創る iCARE

BLOG

PunditとCanCanCanの比較

メグミ
2020/06/29

こんにちは!メグミです!

今回は権限管理のgemについて調べたことを書きたいと思います!

Pundit と CanCanCan の2つが人気がありそうだったので、
比較をしてみました?


まずはそれぞれのgemの概要について、

Pundit

https://github.com/varvet/pundit

Githubには、

シンプルで堅牢でスケーラブルな認可システムを作ることができるgem

とあります。

CanCanCan

https://github.com/CanCanCommunity/cancancan

Githubには、

認可のロジックを一箇所に保持し、簡単にメンテナンスやテストができるgem

とあります。


ざっくり紹介するとこんな感じです。
次はそれぞれのポイントごとに比較をしてみます。

導入

Pundit

class ApplicationController < ActionController::Base
    include Pundit
end

gemをインストールし、コントローラーでモジュールを include することで使用できる。

CanCanCan

gem をインストールし、Abilityクラス(※詳細は後述)を設置すれば使用可能。
コントローラーでモジュールをincludeをする必要はない。

権限の設定

以下2つのデータモデルに対する権限の設定の場合

  • Post
  • Dashboard

Pundit

class PostPolicy
    attr_reader :user, :post

    def initialize(user, post)
        @user = user
        @post = post
    end

    def update?
        user.admin?
    end
end
class DashboardPolicy
    attr_reader :user, :dashboard

    def initialize(user, dashboard)
        @user = user
        @dashboard = dashboard
    end

    def update?
        user.admin?
    end
end

各モデルに対応したPolicyファイルを作成し、権限ごとの設定を記述する。

CanCanCan

class Ability
    include CanCan::Ability

    def initialize(user)
        can :read, Post, public: true

        if user.admin?
            can :update, Post
            can :update, Dashboard
        end
    end
end

Abilityクラスを追加し、各モデルに対するすべての権限を記述する。

権限の認可

Pundit

def update
    @post = Post.find(params[:id])
    authorize @post
    if @post.update(post_params)
        redirect_to @post
    else
        render :edit
    end
end

authorize メソッドを使うと、モデル(この場合Postモデル)に対応するPolicyクラス(この場合PostPolicy)が自動的に参照される。

class PostPolicy < ApplicationPolicy
    def update?
        user.admin?
    end
end

コントローラーのアクション名(update)とマッチしたPolicyのメソッド(update?)が呼ばれ、そのアクションが実行可能か判定する。

CanCanCan

def update
    @post = Post.find(params[:id])
    authorize! :update, @post
end

authorize! メソッドで権限の認可ができる。

class Ability
    include CanCan::Ability

    def initialize(user)
        can :read, Post, public: true

        if user.admin?
            can :update, Post
            can :update, Dashboard
        end
    end
end

Abilityクラスに定義した権限設定を参照し、権限判定する。

権限の判定

Pundit

<% if policy(@post).update? %>
    <%= link_to "Edit post", edit_post_path(@post) %>
<% end %>

policyメソッドを使用することで、権限判定することができる。

CanCanCan

<% if can? :update, @post %>
    <%= link_to "Edit post", edit_post_path(@post) %>
<% end %>

can? や cannot? メソッドを使うことで、権限を判定することができる。

スコープ

Pundit

Pundit では Scope という概念

class PostPolicy < ApplicationPolicy
    class Scope < Scope
        def resolve
            if user.admin?
                scope.all
            else
                scope.where(published: true)
            end
        end
    end
    # 他のメソッド...
end

PolicyファイルにScopeクラスを定義することで特定のユーザーがアクセスできるレコードを制限できる。

def index
    @posts = policy_scope(Post)
end

def show
    @post = policy_scope(Post).find(params[:id])
end

policy_scope メソッドを使ってスコープを利用することができる。

CanCanCan

CanCanCan では Hash of Conditions という概念

can :read, Post, published: true

条件を渡すことで、取得するレコードを絞ることができる。

Post.accessible_by(current_ability)

モデルに対して、 accessible_by メソッドを使うことで、定義した条件のレコードを取得できる。

まとめ

Pundit

特徴

各モデルに対応したPolicyを設置し権限を設定

メリット

データモデルが多い場合でもPolicyファイルはシンプルに保つことができる。

デメリット

それぞれのPolicyファイルを確認しないと権限設定がわからないため、権限の全体が見通しづらい。

CanCanCan

特徴

Abilityクラスの中で各モデルクラスへの権限を設定

メリット

権限の設定はすべてAbilityクラスで定義されているため、権限の全体が理解しやすい。

デメリット

データモデルが多く複雑な権限体系の場合は大量の分岐処理が一つのクラスの中に記述されるため不向き。

さいごに

CarelyではPunditを採用しています!
CanCanCanについては全く知らなかったので勉強になりました!

iCAREでは仲間を募集中です!
興味がある方はこちらからエントリーお待ちしています?