とーますメモ

Ruby on Rails / Goなどの学習メモ

【Activeadmin超入門】CanCanCanの設定 〜その2〜

前回の【Activeadmin超入門】インストール・環境設定 〜その1〜 - とーますメモに続けて、権限管理を行うためのCanCanCanの設定を書いていく。

前の記事にも書いたとおり、CanCanCanはCanCanの後継プロジェクトであり、
設定の仕方はCanCanと同じように記述できるっぽい。(違ったら教えて。)

ちなみに権限を設定する場合、以下の2つのケースがあると思う。

1)1ユーザに、1つの権限を付与
2)1ユーザに、複数の権限を付与

それぞれの場合で、どのように設定するかをメモ書き。
また権限は「admin」と「manager」の2つとする。

1)1ユーザに、1つの権限を付与

①DB変更

まずは下準備に、権限を保存するカラムをdeviseが作成する"admin_users"テーブルに追加する。権限を保存するカラムとして、roleを追加する。このカラムの値のバリエーションはそこまで沢山無いため、データ型はEnumにする。

$ bundle exec rails g migration add_role_to_admin_users role:integer

生成されたマイグレーションファイルに「null: false, default: 2, :after => :encrypted_password」を追加。
自分はカラムの位置も気にするので、encrypted_passwordの後にroleカラムを配置。

class AddRoleToAdminUsers < ActiveRecord::Migration[5.1]
  def change
    add_column :admin_users, :role, :integer, null: false, default: 2, :after => :encrypted_password
  end
end

そしてマイグレーション

$ bundle exec rake db:migrate

②Enum設定

models/admin_user.rbで「enum role: { admin: 1, manager: 2 }」を追加

class AdminUser < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable,
         :recoverable, :rememberable, :trackable, :validatable

  enum role: { admin: 1, manager: 2 }
end

③Abilityクラスの生成

CanCanCanでは権限設定をこのAbilityクラスで行う。

生成方法は以下

$ bundle exec rails g cancan:ability

生成されたability.rbに[admin]と[manager]の権限設定を行う。
以下の例はadminには全権限を与え、
managerには、dashboardページには全権限を与え、
AdminUserページには読込権限のみを与える設定。

app/models/ability.rb

class Ability
  include CanCan::Ability

  def initialize(admin_user)
    admin_user ||= AdminUser.new
    if admin_user.admin?
      can :manage, :all
    elsif admin_user.manager?
      can :manage, ActiveAdmin::Page, :name => "Dashboard"
      can :read, AdminUser
    end
  end
end

ロールに付与できる権限の基本は5つ。
「create、read、update、destroy、manage」
manage以外は、名前の通りの意味。

manageは「CRUD全て」という意味ではないらしく、
独自で設定した権限も含めての全権限という意味らしい。。。(あってるかな?)
詳細は以下のサイト様より。
How to use CanCan / CanCanCan - Qiita

また権限設定は順番が大事で、競合する設定がある場合
最後に適用されたほうが有効になるっぽい。


ちなみに権限にはエイリアスも設定できる。デフォルトでは以下の設定がされている。

alias_action :index, :show, :to => :read
alias_action :new, :to => :create
alias_action :edit, :to => :update

一行目の例で行くと、:indexや:showという権限も:readとして使用できるということだろう。

※[Note] ちなみにactiveadminで作成できるページの種類は、以下の2つ。

1)モデルに紐付いたページ(例:AdminUserページ)
2)モデルに紐付いていない独立したカスタムページ(例:Dashboard)

④current_ability関数のオーバーライド

Abilityクラスのinitializeはコントローラ上に定義された
current_abilityメソッドによって呼び出されるため、
必要に応じて適切な設定をする必要がある。

CanCanCanはcurrent_abilityのデフォルト処理では
現在ログインしているユーザ情報を"current_user"から取得する設定になっているが
activeadminインストール時に、deviseを一緒にインストールすると
ユーザを管理するモデルがAdminUserになるため、現在ログインしているユーザ情報は
"current_user"ではなく"current_admin_user"に入っている。

そのため、current_abilityメソッドの中身を以下のように変更する必要がある。

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception

  def current_ability
    # before
    # @current_ability ||= Ability.new(current_user)

    # after
    @current_ability ||= Ability.new(current_admin_user)
  end

⑤CanCanCanの有効化し、認証されたかった場合のメソッド定義

active_adminの設定ファイル内でCanCanCanを有効化する。

ActiveAdmin.setup do |config|
  ...
  config.authorization_adapter = ActiveAdmin::CanCanAdapter
  config.cancan_ability_class = "Ability"
  config.on_unauthorized_access = :access_denied
  ...
end

1行目でCanCanCanを有効化し、
2行目でCanCanCanの権限管理クラス名を指定し
3行目で認証されなかった場合の処理を行うメソッドを指定する。

認証されたかった場合のメソッド定義(access_denied)は、application_controller.rbに記述

def access_denied(exception)

    # 権限がなくてアクセスできないページはダッシュボードへ飛ばしてメッセージを表示
    # 認証が切れてアクセスできない場合はログイン画面を表示
    if current_admin_user
      redirect_to admin_root_path, alert: I18n.t('active_admin.access_denied.message')
    else
      redirect_to new_admin_user_session_path, alert: exception.message
    end
  end

⑥AdminUserのindex及びformブロックにrole情報を入れる

自分は以下のように設定した。
app/admin/admin_user.rb

ActiveAdmin.register AdminUser do
  permit_params :email, :password, :password_confirmation, :role

  index do
    selectable_column
    id_column
    column :role
    column :email
    column :current_sign_in_at
    column :sign_in_count
    column :created_at
    actions
  end

  filter :email
  filter :current_sign_in_at
  filter :sign_in_count
  filter :created_at

  form do |f|
    f.inputs "Admin Details" do
      f.input :email
      f.input :password
      f.input :password_confirmation
      f.input :role, as: :radio, :collection => [
        ['Admin', 'admin'],
        ['Manager', 'manager']
      ]
    end
    f.actions
  end

end

具体的に追加したのは、
(1)permit_paramsに:roleを追加
(2)indexブロックにcolumn :roleを追加
(3)formブロックにf.input :role...を追加


以上でユーザの追加・編集及び権限管理もできるようになる。

[参考記事]
How to use CanCan / CanCanCan - Qiita
【Rails】ActiveAdmin+CanCanCanで権限毎に色々したい - Qiita
CanCanCanを使ってみる - Qiita
Rails4でCanCanCanによる権限管理 [俺の備忘録]


2)1ユーザに、複数の権限を付与

CanCanCanの設定は1)と似ているが
それ以外が結構違う。

詳細は以下のサイトさんが詳しいので参照されたし。

blog.hello-world.jp.net
sessan.hatenablog.com