掲題の件、最初はcancanを使おうと思ってたのですが開発がとまっているようでしたので、
Rails4 + Devise + Authority + Rolifyで管理者権限付きの管理画面を作成しました。
開発も活発で、使いやすかったため、今後また別の案件で使いたいと思ったのでメモに残したいと思います。
#簡単な流れ
gem 'devise'
gem 'authority'
gem 'rolify'
-
Deviseのインストールおよび設定(省略)
rails generate devise:install rails generate devise User
-
Authorityのインストール
rails generate authority:install
Authorityは、実際に各controllerのactionごとに、そのactionを実行できる条件を記述したりすることができます。
またUserが指定した権限をもっているかどうかをチェックしたりできます。
/app/authorizers/application_authorizer.rb
/config/initializers/authority.rb
が作成されます。 -
Rolifyのインストール
rails generate rolify:role
Rolifyは、Userにroleを与えたり、指定したroleをもっているUserを探したりできます。
たぶん4のスクリーンショットをみれば何となくどんな役割かは分かるかと思います。
/config/initializers/rolify.rb
が作成されます。
この2つのテーブル+deviseで作成されるUserテーブルが作成されます。
#Authority + Rolifyの設定
基本的にはデフォルトの状態で良いかと思います。
Authority.configure do |config|
config.controller_action_map = {
:index => 'read',
:show => 'read',
:new => 'create',
:create => 'create',
:edit => 'update',
:update => 'update',
:destroy => 'delete'
}
# 各actionがread create update deleteのどれに当てはまるか記載する。
config.abilities = {
:create => 'creatable',
:read => 'readable',
:update => 'updatable',
:delete => 'deletable'
}
# deviseでcontrollerの継承を行っている場合に必要になることがある。
# 例えば、deviseのデフォルトの'current_user'を、'current_admins_user'などとしている場合に、
# 'current_admins_user'でAuthorityを利用できるようになる。
config.user_method = :user_for_this_request
end
# Other authorizers should subclass this one
class ApplicationAuthorizer < Authority::Authorizer
# 各controllerごとに権限管理をしたい場合は、application_authorizer.rbを継承します。
# 以下は、権限を設定していないactionのデフォルトの設定になります。
def self.default(adjective, user)
true
end
end
以下は、ApplicationAuthorizerを継承したUserAuthorizerになります。
以下の例の場合は、
update(edit update) create(create new) read(index show) はadminまたはeditorにアクセス権がある
delete(destroy)はadminにのみアクセス権がある
となります。
手動でファイル作成。
class UserAuthorizer < ApplicationAuthorizer
def self.updatable_by?(user)
user.has_role?(:admin) || user.has_role?(:editor)
end
def self.creatable_by?(user)
user.has_role?(:admin) || user.has_role?(:editor)
end
def self.readable_by?(user)
user.has_role?(:admin) || user.has_role?(:editor)
end
def self.deletable_by?(user)
user.has_role?(:admin)
end
end
モデルに記述を追加します。
class User < ActiveRecord::Base
# creatable_by?(user)などのメソッドが利用できるようになる。
include Authority::Abilities
# can_create?(resource)などのメソッドが利用できるようになる。
include Authority::UserAbilities
# 権限の設定は、/app/authorizers/user_authorizer.rbを参照する
self.authorizer_name = 'UserAuthorizer'
# roleを適用したいmodelの中に記載
resourcify
#以下のコールバックが利用できるようになる。
rolify
# Userを追加した際にデフォルトでeditor roleを割り当てるための設定。
# 他にも、削除する前、削除した後、作成する前、作成した後といったコールバックが用意されている。
after_create :assign_default_role
def assign_default_role
self.add_role(:editor)
end
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable#, :confirmable
end
controllerに記述を追加します。
僕は管理画面作成のためにadminsコントローラーを継承させています。
class Admins::UsersController < AdminsController
alias :user_for_this_request :current_admins_user
# show action以外は、user_authorizer.rbを使用する
authorize_actions_for User, except: :show
def index
@users = User.all
end
def show
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
@user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params
.require(:user)
.permit(:email, :password, :password_confirmation)
end
end
deviseのcontrollerを継承させて、拡張している場合はこちらにも記載を。
class Admins::Users::RegistrationsController < Devise::RegistrationsController
alias :user_for_this_request :current_admins_user
authorize_actions_for User
end
その他viewやエラーハンドリングなど
application_controllerでエラーを捕そくして、「アクセス権がありません」などといったページを表示させるといいかと思います。
class ApplicationController < ActionController::Base
rescue_from Authority::SecurityViolation, with: :authority_forbidden
def authority_forbidden(error)
Authority.logger.warn(error.message)
render file: "#{Rails.root}/public/403.html", status: 403, layout: 'error'
end
end
viewでも、例えば削除権限のないUserに対しては、削除ボタンなんかは非表示にしておくと親切ですね。
- if current_admins_user.has_role? :admin
#管理者User用の処理
- else
#それ以外のUser用の処理
ファイル内にコメントアウトで説明があるので分かるかと思いますが、
ActiveRecordを利用しない場合や、user.is_admin?のようなショートカットを設定したい場合などには、以下のようにするそうです。(動作未確認)
Rolify.configure do |config|
config.use_mongoid
config.use_dynamic_shortcuts
end
参照
AuthorityにはAuthorizerメソッドの優先順位があるとのことで、これについては既に詳しく記事にしてくださっている方がおりました。感謝。
authority gem の Authozier メソッドの優先順位について
github Rolify
github Authority