adminを設けてアプリを管理する方法(Pundit + rails_admin)
当初の課題感
いま、Railsのアプリを開発しており、
admin権限を設けて、自分のアプリのテーマに沿わないユーザーをadmin権限で削除する機能を実装したいとおもったので、やり方も含めてメモしていきます!
まずはリサーチ
色々、調べて結果、やるべきことが2つあることが分かった。
1.admin権限を与える
2.管理画面系のUIを実装
まず、
1の方で、「あなたは、管理者か?、ただのユーザーか?」を問う記述を施して、
2の方で、「管理者なら、この管理画面をどうぞ」と特別なGUIの操作パネルを用意する感じ。
導入方法の記事で参考になりそうなものは下記。
・rails_adminを使用した管理画面の作成(cancancan + rails_admin)
・Rails_adminで管理者機能作成(cancancan + rails_admin)
・Ruby on Rails における権限Gem まとめ と GlobalState | Offers Tech Blog(1で使えるgemの比較記事)
実装方針
リサーチを元に、1はpundit、2はrails_adminのgemを活用して実施することにしました。
理由は、
1は、cancancanの記事を観ていると、policyファイルの管理がファットになってしまって大変だったという意見があったため。puditは使いやすい慣れやすいという意見もあったので、開発機関から逆算して素早く実装できる方法で扱いやすい方法を選択しました。
2も同じ理由で、素早く実装できて扱いやすい方法として選択しました。
Punditの実装
まずは、公式READMEを読む。
READMEに沿って、インストールします。
gem "pundit"
budle install
インストールしたら、application_controllerに下記を追加
class ApplicationController < ActionController::Base
・・・
include Pundit::Authorization
・・・
end
このgemにはポリシーファイルのジェネレーターが実装されているので、実行します。
rails g pundit:install
そうすると、policies
というフォルダと、中にapplication_policy.rb
が生成されるはずです。ここに記述を施して、adminやユーザーに関する制限を加えていきます。
その前に、Userモデルにadminカラムを作成して、各ユーザーに管理者か一般ユーザーなのかを見分ける施しをしておきます。
rails g migration AddAdminToUsers admin:boolean
デフォルトで、falseにしておきます。(一般ユーザーにしておく)
class AddAdminToUsers < ActiveRecord::Migration[7.2]
def change
add_column :users, :admin, :boolean, default: false
end
end
rails db:migrate
ユーザーを1つだけ、admin権限をもたせましょう。私はid=1をadminにします。
rails c
# adminを付与
user = User.first
user.update_attribute(:admin, true)
# 下記で確認して、admin: trueになっていればOK!
User.first
# 退出
exit
rails_admin
まずは、公式READMEを読む。インストール方法は、READMEのInstallationにある。(Getting Startedのどドキュメントをみてしまって時間取られた(一敗))
公式通りインストールします。
分からなかった時用の、日本語記事も貼っておきます。
gem "rails_admin", "~> 3.0"
bundle install
# 終わったら公式のインストールコマンドを実行
rails g rails_admin:install
# こういうふうに聞かれると思いますが、adminの名前空間で良ければEnterを押すだけ
? Where do you want to mount rails_admin? Press <enter> for [admin] >
ここまで終わると、routes.rb
に変化と、rails_admin.rb
という新規作成ファイルが出現します。
まず、rails_admin.rb
を修正します。(デフォルトでメジャーなgemはコメントを外すだけになっているので楽ですね!)
RailsAdmin.config do |config|
config.asset_source = :sprockets
### Popular gems integration
## == Devise == このコメントアウトを外しました。
config.authenticate_with do
warden.authenticate! scope: :user
end
config.current_user_method(&:current_user)
## == CancanCan ==
# config.authorize_with :cancancan
## == Pundit == このコメントアウトを外しました。
config.authorize_with :pundit
## == PaperTrail ==
# config.audit_with :paper_trail, 'User', 'PaperTrail::Version' # PaperTrail >= 3.0.0
### More at https://github.com/railsadminteam/rails_admin/wiki/Base-configuration
## == Gravatar integration ==
## To disable Gravatar integration in Navigation Bar set to false
# config.show_gravatar = true
config.actions do
dashboard # mandatory
index # mandatory
new
export
bulk_delete
show
edit
delete
show_in_app
## With an audit adapter, you can add:
# history_index
# history_show
end
end
ここまで終わったら、localhost:3000/admin
にアクセスしてみましょう。
下記の画面が出るはず。(出ない人は、サーバーやdockerの再起動を試してみてください)
punditで生成されたapplication_policy.rb
に記述が必要です。
# frozen_string_literal: true
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
@user = user
@record = record
end
# dashboardが許可されていなかったのでそれを渡してあげます。
def dashboard?
user.present? && user.admin?
end
def index?
false
end
・・・
end
これでダッシュボードが映るはずです!
例)管理者ダッシュボードでUserモデル一覧を表示する
出たのは良いものの、「無」の状態で何もできないので、例としてUserモデル一覧をダッシュボードで表示してみましょう。
正しい方法では無いと思うですが、自分なりのやり方です。
1.◯◯_policy.rbファイルを作成し、ApplicationPolicyを継承する。
2.localhost:3000/adminをページ更新。エラーと地道に格闘。
説明していきます。
1.◯◯_policy.rbファイルを作成し、ApplicationPolicyを継承する。
表題通り、Userモデル一覧を管理者ダッシュボードで表示させたいので、user_policy.rb
を作成します。
application_policy.rb
を複製して名前を変えると楽です。下記は複製したうえで少し修正したものです。
# frozen_string_literal: true
class UserPolicy < ApplicationPolicy
def index?
user.admin?
end
def show?
user.admin? || user == record
end
def create?
user.admin?
end
def new?
create?
end
def update?
user.admin? || user == record
end
def edit?
update?
end
def destroy?
user.admin? && user != record
end
class Scope < Scope
def resolve
if user.admin?
scope.all
else
scope.none
end
end
end
end
2.localhost:3000/adminをページ更新。エラーと地道に格闘。
これでページを更新するとエラーが出ます。大抵出るエラーが「◯◯?っていうメソッドが無いよ!」というものです。以下の写真は実際に出たエラー画面。
エラー画面に言われるままに、メソッドを追加します。
中身は、user.present? && user.admin?
を書いていきます。(要はadminか?というのを確認する内容のメソッドにします)
例えば、上のエラー画面の解決はこんな感じ。
# frozen_string_literal: true
class UserPolicy < ApplicationPolicy
・・・
def export?
user.present? && user.admin?
end
・・・
end
ページをリロードすると、また「◯◯?メソッドが無いよ!」と言われるので、繰り返します。
そうすると、表示されます!
もしTailwind-railsを使っていると起こるRspecが回らない問題の解決
こちらのissueの通りにやれば解決です。感謝。ぜひ参考にしてみてください。