Rails4 + Devise + Authority + Rolifyで管理者権限付きの管理画面作成

掲題の件、最初はcancanを使おうと思ってたのですが開発がとまっているようでしたので、
Rails4 + Devise + Authority + Rolifyで管理者権限付きの管理画面を作成しました。
開発も活発で、使いやすかったため、今後また別の案件で使いたいと思ったのでメモに残したいと思います。

#簡単な流れ

[こちらのサイトを参考にしました](https://github.com/EppO/rolify/wiki/Using-rolify-with-Devise-and-Authority)

```lang:Gemfile
gem 'devise'
gem 'authority'
gem 'rolify'
```

1. Deviseのインストールおよび設定(省略)
`rails generate devise:install
rails generate devise User`

2. Authorityのインストール
`rails generate authority:install`
Authorityは、実際に各controllerのactionごとに、そのactionを実行できる条件を記述したりすることができます。
またUserが指定した権限をもっているかどうかをチェックしたりできます。
/app/authorizers/application_authorizer.rb
/config/initializers/authority.rb
が作成されます。

3. Rolifyのインストール
`rails generate rolify:role`
Rolifyは、Userにroleを与えたり、指定したroleをもっているUserを探したりできます。
たぶん4のスクリーンショットをみれば何となくどんな役割かは分かるかと思います。
/config/initializers/rolify.rb
が作成されます。

4. Migration
`rake db:migrate`
![スクリーンショット 2014-04-06 17.14.44.png](https://qiita-image-store.s3.amazonaws.com/0/10352/f5a3629b-72f1-3912-f5fc-bce1d0e7a30f.png)

この２つのテーブル+deviseで作成されるUserテーブルが作成されます。

#Authority + Rolifyの設定
基本的にはデフォルトの状態で良いかと思います。

```lang:/config/initializers/authority.rb
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
```

```lang:/app/authorizers/application_authorizer.rb
# 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にのみアクセス権がある*
となります。
手動でファイル作成。

```lang:/app/authorizers/user_authorizer.rb
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
```


モデルに記述を追加します。

```lang:/app/models/user.rb
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コントローラーを継承させています。

```lang:/app/controllers/admins/users_controller.rb
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を継承させて、拡張している場合はこちらにも記載を。

```lang:/app/controllers/admins/users/registrations_controller.rb
class Admins::Users::RegistrationsController < Devise::RegistrationsController
  alias :user_for_this_request :current_admins_user
  authorize_actions_for User
end
```


# その他viewやエラーハンドリングなど
application_controllerでエラーを捕そくして、「アクセス権がありません」などといったページを表示させるといいかと思います。

```lang:/app/controllers/application_controller.rb
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に対しては、削除ボタンなんかは非表示にしておくと親切ですね。

```lang:/app/views/admins/users/index.html.slim

- if current_admins_user.has_role? :admin
  #管理者User用の処理
- else
  #それ以外のUser用の処理

```




最終的にこんな権限表を用意して見やすくしてみました。
![スクリーンショット 2014-04-06 18.33.11.png](https://qiita-image-store.s3.amazonaws.com/0/10352/eb49912d-ec3a-18ed-31a4-b77612faf0fd.png)


ファイル内にコメントアウトで説明があるので分かるかと思いますが、
ActiveRecordを利用しない場合や、user.is_admin?のようなショートカットを設定したい場合などには、以下のようにするそうです。（動作未確認）

```lang:/config/initializers/rolify.rb
Rolify.configure do |config|
  config.use_mongoid
  config.use_dynamic_shortcuts
end
```


# 参照
AuthorityにはAuthorizerメソッドの優先順位があるとのことで、これについては既に詳しく記事にしてくださっている方がおりました。感謝。
[authority gem の Authozier メソッドの優先順位について](http://qiita.com/toshiwo/items/9303f6f6b86b37cea42f)
[github Rolify](https://github.com/EppO/rolify)
[github Authority](https://github.com/nathanl/authority)