👌

CanCanCan で少し複雑な権限管理

2024/04/16に公開

初めに

以前紹介したCMSに、よくある承認フローだったり、閲覧しかできないユーザ、他者が登録したのレコードに対する制限だったりの機能が必要になったため、備忘録としてまとめておきます。(いつもこの辺りで実装迷うので・・・)

ActiveAdmin + CanCanCan での権限管理

前提条件

  • Rails 7.x 系
  • 管理画面はActiveAdminを導入している
  • 伴って、基本的にCanCanCanを使用する
  • 複数コンテンツのレコードは同一テーブルで管理
  • 参考CanCanCanの基本的な使い方

必要な機能

  • コンテンツは管理画面から追加・削除が可能
  • コンテンツごとに下記権限が必要
    • 閲覧不可
    • 登録可能
    • 他者作成レコード操作可能
    • 承認可能

テーブル構成

  • コンテンツ(Content)
    • ニュース、ブログ等コンテンツを管理するテーブル
      id, name, created_at, updated_at
      
  • コンテンツレコード(ContentRecord)
    • コンテンツのレコードを保持するテーブル
      id, content_id, create_admin_user_id, body, status, created_at, updated_at
      
  • 管理者(AdminUser)
    • ログインアカウント
      id, email, password, role_id
      
  • 権限マスタ(Role)
    • 権限マスタ
      id, name, created_at, updated_at
      
  • 権限詳細(RoleDetail)
    • コンテンツ毎の詳細を保持する子テーブル
      id, role_id, content_id, content_role
      

コード実装

models/ability.rbの設定

Contents.all.each do |c|
    # コンテンツの権限詳細が設定されているかどうか
    content_role = user.role.role_details.find_by(content_id: c.id)
    next if content_role.blank?

    case content_role
    when :writer
        can :create, Content, content_id: c.id # このコンテンツを登録可能
        can [:read, :update, :delete], Content, content_id: c.id, create_admin_user_id: user.id # 自身のレコードの操作可能
    when :manager
        can :create, Content, content_id: c.id # このコンテンツを登録可能
        can [:read, :update, :delete], Content, content_id: c.id # レコードの操作可能
    when :approver
        can :create, Content, content_id: c.id # このコンテンツを登録可能
        can [:read, :update, :delete], Content, content_id: c.id # レコードの操作可能
    end
end

これで、コンテンツ毎のライター、マネージャー、承認者権限の設定ができました。
ただこれではまだ承認フローが実装できていません。
CanCanCanでアクセス可能属性を設定することもできるようですが、自分の場合はコントローラにロジックを記載しました。

admin/content_records.rbの実装

form do |f|
〜
    if approver? # ログインユーザがこのコンテンツの承認者かどうか判定
      f.inputs do
        f.input :status 
      end
    end
〜
end

controller do
    def create
        unless approver? # 承認者以外の場合、 statusを除外しておく
            params[:content_record].delete(:status)
        end
        super
    end

    def update
        unless approver? # 承認者以外の場合、 statusを除外しておく
            params[:content_record].delete(:status)
        end
        super
    end
end

これで、承認者以外はstatusカラムを操作できなくなりました。
一旦これで、コンテンツ毎に4種類の権限を設定できるようになりました。
あとは role, role_details を管理する画面と、admin_userのroleを設定できるようにカスタマイズすれば、たったこれだけのコードで承認フロー、権限管理を導入することができました。
めでたしめでたし

おまけ

実際に実装した権限設定画面のSS

Discussion