Red > Green > Refactor > Red

cycle is based on desire

権限管理 gem ; cancan

Devise に次いでの衝撃

Devise が認証機能を持たせる gem に対して、

cancan は認証後の権限を管理する gem

最終的にやること

cancan を用いて 登録 email 権限ごとに挙動 を変化させること

前提条件

rails g devise user

からの流れで

ユーザー1 matsu@matsu.jp
ユーザー2 take@take.jp

が Devise gem を使った users テーブルに保存されている。

rails g scaffold task name:string
rake db:migrate

をしていること


準備/環境

Gem file に cancan を添える

gem 'cancan'

モデル

権限管理をするための Model クラスを作成。gem の設定上 Ability Modelを

設置しなければいけないらしい。既存 Model と衝突しないように。

$ rails g cancan:ability
create  app/models/ability.rb

どのように権限をつけるか設定をする

設定箇所は initialize 内

app/models/ability.rb

class Ability
  include CanCan::Ability

  def initialize(user)
  end 
end

設定例

登録 user の email が 'matsu' とマッチしていたら「権限」を持っていると設定する。

app/models/ability.rb

class Ability
  include CanCan::Ability

  def initialize(user)
    if user.email =~ /matsu@.*/
    ###
    else
    ###
    end
  end 
end

具体的にどう権限を持たせるか

各 controller 中のCRUD(create, read, update, delete)で

制限をかける。もしくは 全権利(management)を割り当てる。

app/models/ability.rb

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new # 新規ユーザーの為に or 代入が必須
    if user.email =~ /matsu@.*/
      can :manage, Task # matsu@.* のユーザーは task controller の全権限を持つ
    else
      can :read, Task # matsu@.* 以外のユーザーは task controller の read だけ権限を持つ
    end
  end 
end

コントローラ

TasksController に権限管理を定義したメソッドを付与する

app/views/tasks_controller.rb

TasksController < ApplicationController
  load_and_authorize_resource # このメソッドを付与する
  ...
end

cancan 用 マスアサインメント対応

app/controller/application_controller.rb

class ApplicationController < ActionController::Base
  before_filter do
    resource = controller_name.singularize.to_sym
    method = "#{resource}_params"
    params[resource] &&= send(method) if respond_to?(method, true)
  end 
  protect_from_forgery with: :exception
end

各コントローラ側のマスアサインメント用paramsの値は、各々設定。下の例は tasks

def tasks_params
  params.require(:tasks).permit(:name)
end

ビュー

Tasks の view に次の if-else 分岐を書く

app/views/tasks/index.html.erb

<h1>Listing tasks</h1>

<% if can? :destroy, Task %>
  I can destroy user!
<% else %>
  I cannot destroy user ...!
<% end %>

<table>
  <thead>

matsu@matsu.com でログインして/tasksに遷移すると、

I can destroy! が表示される。tasks controller のどこにでもアクセスできる。

take@take.com でログインして/tasksに遷移すると

I cannot destroy user ...! が表示される(未ログイン状態でも同じ)

tasks controller の read, create にしかアクセスできない。

destroy をしようとすると 警告 You are not authorized to access this page.

が表示されて destroy できない。


very hot な cool gem

マスアサインメント引用

https://github.com/ryanb/cancan/issues/835#issuecomment-18663815