CanCan を使うとユーザモデルが各リソースに対してアクセスする際の CRUD 権限を付与できるが、
ユーザモデルが複数になった場合、それぞれに異なった権限を設定したくなるかと思います。
例
User モデルと Admin モデルがそれぞれ以下のような役割を持つ
■User
・一般ユーザには各リソースに対して参照権限と修正権限を付与
・特別なユーザには各リソースに対して作成権限、参照権限、修正権限、削除権限の全てを付与■Admin
・カスタマーサポートには各リソースに対して参照権限を付与
・サービス管理者には各リソースに対して参照権限、修正権限、削除権限を付与
このような場合は、Ability クラスを複数に分けて権限を設定すると、幸せになれます。
モデルを作成して役割を与える
今回はユーザモデルの作成に devise を使用し、役割の判別は Rails 4.1 より追加された enum を使いますが、もちろん他の方法でも問題ありません。
$ rails generate devise User
$ rails generate devise Admin
$ rails generate migration AddRoleToUsers role:integer
$ rails generate migration AddRoleToAdmins role:integer
$ rake db:migrate
class User < ActiveRecord::Base
enum role: {general: 1, special: 2}
end
class User < ActiveRecord::Base
enum role: {support: 1, manager: 2}
end
権限を設定する
権限設定には CanCan を引き継いだ CanCanCan を使用し、設定は複数の Ability クラスを作成して行います。
$ rails generate cancan:ability
create app/models/ability.rb
$ cp -ip app/models/ability.rb app/models/user_ability.rb
$ cp -ip app/models/ability.rb app/models/admin_ability.rb
class UserAbility
include CanCan::Ability
def initialize(user)
if user.general?
can :read, :all
can :update, :all
elsif user.special?
can :manage, :all
end
end
end
class AdminAbility
include CanCan::Ability
def initialize(admin)
if admin.support?
can :read, :all
elsif admin.manager?
can :read, :all
can :update, :all
can :destroy, :all
end
end
end
コントローラで適用する権限設定を振り分ける
ここが最も重要になりますが、CanCan::ControllerAdditions
の current_ability
を上書きします。
class ApplicationController < ActionController::Base
def current_ability
if user_signed_in?
@current_ability ||= ::UserAbility.new(current_user)
elsif admin_signed_in?
@current_ability ||= ::AdminAbility.new(current_admin)
else
@current_ability ||= ::Ability.new(nil)
end
end
end
この設定を入れることで、User でログインしている場合は UserAbility に設定した権限が適用され、
Admin でログインしている場合は AdminAbility に設定した権限が適用されます。
なお、未ログイン状態のユーザに対しては、Ability に設定した権限が適用されます。