[rails]ActiveAdminとCanCanCanを使って管理画面に権限機能をつける
Ruby2.1.2, Rails4.1.5で確認
ActiveAdminで作成した管理画面に、ログインユーザによってアクセスできる機能を出し分ける権限機能をつけたかったので調べてみました。
以前は「 cancan」と組み合わせることで権限管理を実現できたそうですが、cancanは開発が止まってしまったらしいので、Rails4対応の「 cancancan」を使ってみることにしました。
ログイン等の基本的なActiveAdminの機能はすでに動作している想定で進めます。
1. Gemfileに追加
gem 'activeadmin', github: 'activeadmin'
gem 'cancancan', '~> 1.9' # 追加
2. 権限管理用モデルを追加
一つの管理者アカウントに複数の権限を割り当てられるようにするため、 「 ActiveAdminにCanCanを使った権限機能を追加する(1)」を参考にさせていただき、
- admin_role
- admin_role_assign
モデルを追加します
- モデル生成
./bin/rails g model admin_role
./bin/rails g model admin_role_assign
- admin_roleのmigration編集
# db/migrate/xxxxxx_create_admin_roles.rb
class CreateAdminRoles < ActiveRecord::Migration
def migrate(direction)
super
# migrate時にsuper_admin権限を追加
AdminRole.create!(name: 'super_admin') if direction == :up
end
def change
create_table :admin_roles do |t|
t.string :name
t.timestamps
end
end
end
- admin_role_assignのmigration編集
# db/migrate/xxxxxx_create_admin_role_assigns.rb
class CreateAdminRoleAssigns < ActiveRecord::Migration
def migrate(direction)
super
# migrate時に初期管理ユーザにsuper_admin権限を付与
AdminRoleAssign.create!(admin_role_id: AdminRole.first.id, admin_user_id: AdminUser.first.id) if direction == :up
end
def change
create_table :admin_role_assigns do |t|
t.integer :admin_role_id
t.integer :admin_user_id
t.timestamps
end
add_index(:admin_role_assigns, :admin_role_id, { :name => :index_admin_role_assigns_on_admin_role_id })
add_index(:admin_role_assigns, :admin_user_id, { :name => :index_admin_role_assigns_on_admin_user_id })
end
end
- migrate実行
./bin/rake db:migrate
3. モデルの関連付け
- app/models/admin_role.rb
class AdminRole < ActiveRecord::Base
has_many :admin_role_assigns
has_many :admin_users, through: :admin_role_assigns
end
- app/models/admin_role_assign.rb
class AdminRoleAssign < ActiveRecord::Base
belongs_to :admin_user
belongs_to :admin_role
end
- app/models/admin_user.rb
class AdminUser < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable,
:recoverable, :rememberable, :trackable, :validatable
has_many :admin_role_assigns
has_many :admin_roles, through: :admin_role_assigns
def display_name
self.email
end
end
display_nameメソッドはfilter等で表示する際の表示名を表すメソッドで、display_nameメソッドを定義しなかった場合は、モデル名が表示されてしまいます。
activeadminでは lib/active_admin/application.rb に以下のような設定が定義されており、下記リスト内のメソッドを順にチェックして該当するメソッドの結果を表示するようになっています。
# Active Admin makes educated guesses when displaying objects, this is
# the list of methods it tries calling in order
setting :display_name_methods, [ :display_name,
:full_name,
:name,
:username,
:login,
:title,
:email,
:to_s ]
モデルが該当するメソッドを持っていない時は、明示的にメソッドを追加して正しく表示されるようにします。
4. 権限の管理画面を追加
- 権限管理ができるように管理画面を追加
./bin/rails g active_admin:resource AdminRole
- 管理画面からデータ追加できるようにpermit_paramsを設定
# app/admin/admin_role.rb
ActiveAdmin.register AdminRole do
permit_params :name
end
5. admin_user編集時に権限を追加できるように
- app/admin/admin_user.rb
ActiveAdmin.register AdminUser do
permit_params :email, :password, :password_confirmation, admin_role_ids: []
index do
selectable_column
id_column
column :admin_roles do |f|
f.admin_roles.size > 0 ? f.admin_roles.map { |r| r.name }.join(", ") : ''
end
column :email
column :current_sign_in_at
column :sign_in_count
column :created_at
actions
end
filter :email
filter :admin_roles
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 :admin_roles
end
f.actions
end
# パスワードが空の状態で編集した場合はパスワード更新しないように(can't be blank エラーの防止)
controller do
def update
if params[:admin_user][:password].blank?
params[:admin_user].delete("password")
params[:admin_user].delete("password_confirmation")
end
super
end
end
end
6. cancanのAbilityクラスを作成
- generateコマンドを使ってcancancan用のクラスを作成
./bin/rails g cancan:ability
- abilityクラスで権限情報を管理
今回、管理画面にログインできるユーザは
- Dashboardにはアクセス可能
- super_admin権限を持つ管理ユーザはすべての機能を管理可能
- admin権限を持つ管理ユーザは、AdminUserの管理とAdminRoleの管理は不可
という権限管理にしました
これをAbilityクラスに書くと以下のようになります。
class Ability
include CanCan::Ability
def initialize(admin_user)
admin_user.admin_roles.each { |role| send(role.name.downcase) }
can :manage, ActiveAdmin::Page, :name => "Dashboard"
end
def admin
super_admin
cannot :manage, AdminUser
cannot :manage, AdminRole
end
def super_admin
can :manage, :all
end
end
権限の設定方法に関しては 「 Defining Abilities」 が参考になります。
2015/2/17追記
active_adminのinitializersでCanCanAdapterを許可する設定が抜けていたので追記しておきます。
# config/initializers/active_admin.rb
# コメントアウトを解除
config.authorization_adapter = ActiveAdmin::CanCanAdapter
追記ここまで
7. 確認
migrate実行時に登録されるデフォルトの管理ユーザにはsuper_admin権限が付与された状態になっているので、管理画面ログイン後、管理ユーザの編集や権限の編集が可能です。
admin権限を作成し、admin_user追加画面で以下のようにadmin権限を付与してユーザを追加すれば、ユーザ管理権限を持たない管理ユーザを作成することができます。
これでActiveAdminとCanCanCanを用いて、権限管理機能つきの管理画面をつくることができました。