山奥の自宅勤務エンジニアブログ

システム開発技術、データ分析関連でお勉強したことや、山奥生活を綴る、テンション低めなブログです。

ActiveAdminにCanCanを使った権限機能を追加する(3)

作業の流れ

  1. ApplicationControllerに権限がないページにアクセスしたときのエラー制御を追加
  2. ApplicationControllerにAdminUserの権限を初期化する処理を追加
  3. ActiveAdminの各種画面に権限制御を入れていく

1. ApplicationControllerに権限がないページにアクセスしたときのエラー制御を追加

ApplicationControllerに以下のエラー制御を追加する。

  rescue_from CanCan::AccessDenied do |exception|
    redirect_to admin_dashboard_path, :alert => exception.message
  end

2. ApplicationControllerにAdminUserの権限を初期化する処理を追加

ApplicationControllerに以下の権限初期化処理を追加する

  def current_ability
    @current_ability ||= Ability.new(current_admin_user)
  end

3. ActiveAdminの各種画面に権限制御を入れていく

3.1. ActiveAdmin-CanCanの権限制御を自動で行うinitializerの追加

本当は、一個ずつのリソース毎にメニューを表示するかどうか、設定していくことが出来るんだけど、面倒くさいので、一括でやりたいと思って調べたら、本家に以下のようなパッチが出ていた。

Deep integration of CanCan into ActiveAdmin — Gist

上のinitializerを置けば、権限に応じてActiveAdminのメニューやdefault_actionボタンなどよろしくやってくれる。以下の機能があるらしい。

  1. 良い感じにリソースをロードして認証してくれる
  2. メニューはアクセス権限があるときだけ表示される
  3. インデックスページの右側のデフォルトアクションボタンも権限に応じて表示してくれる
  4. リソースページの右上のメンバーアクションも権限に応じて表示してくれる
  5. auto_linkヘルパーを使うと、権限に応じたリンクを自動的に作ってくれる。

※ただし、私が試したところ、上記のinitializerで最後のResourceControllerの定義を書くとなぜかrails初期化時にdevise周りでエラーが発生したので、ResourceControllerの定義のところだけコメントアウトした。コメントアウトした後動作確認したけど、上記3, 4は動作しているように見えた。1はそもそも何のことを言っているのか、よくわからない。2は動作しなかったので後で手動で制御することにした。

※上記サイトに記載されているadd_default_action_itemsのなかで、以下のようなところがあるが、これは誤植?かもしれなく、

:model => active_admin_config.resource_label

↑ではなく、# lib/active_admin/resource/action_items.rbと同じように、

:model => active_admin_config.resource_name

としたら、正しく動作した。

あと、ActiveAdminのメニューなどを見やすくするために、as:を使っている人やnamescpace付きのクラスをActiveAdminに登録している場合、以下のようにしていると思うが、

ActiveAdmin.register My::Model do
end

ActiveAdmin.register Model, as: "Something Else" do
end

こういうのがあるとエラーが出てしまうので、以下のパッチを追加しておく。

module CanCan
  class ControllerResource
    # Just use the active admin resource_class
    def resource_class
      @controller.resource_class
    end
  end
end

参考:Deep integration of CanCan into ActiveAdmin — Gistのコメント部分。

3.2. 権限のないメニューを消す

権限のないリソースに対しては、画面上部のメニューを出さないようにしたい。

以下のようなコードをActiveAdminのモデル登録用の各クラスに以下のように入れていく。

ActiveAdmin.register AdminRole do
  menu :parent => "Admin" , :if => proc {can?(:manage, AdminRole) }
  
end

3.3. URLで直打ちで表示できないように、認可を各ページに入れる

おそらく、コメントアウトしたResourceControllerのところで実行されるはずだった以下のコードは自分で実行しないと行けない。

    load_resource :except => :index
    authorize_resource

実際は、ActiveAdminのモデル登録用の各クラスに以下のように入れていく。

ActiveAdmin.register ClassName do
  controller do
    load_resource :except => :index
    authorize_resource
  end
end

これでメニューに出ていないリソースにURL直打ちでアクセスしたとしても、エラーメッセージが出て表示されない。

3.4. ダッシュボードに表示している内容も権限毎に変える

ダッシュボードでsectionなどを定義している場合は、今ログインしている人の権限に応じて、以下のような制御を入れる。

section "summary", :if => Proc.new {current_admin_user.has_role? :admin} do
  link_to "hoge"
end