【備忘録】Rails6 Devise ＋ cancancan の使い方



##はじめに

deviseを使用してUserモデル（認証）を作成後して
cancancanを追加して認可をできるようにしました。

その過程を備忘録としてログします。




deviseのgemを追記します。


```Gemfile
・
・
・
gem 'devise'

```

バンドルインストールします。

```
$bundle install
```

デバイスをインストールします。

```
$rails generate devise:install
```

設定があるか確認します。

```config/environments/development.rb
・
・
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
```


page#indexを追記します。

```config/routes.rb
Rails.application.routes.draw do
root to: "page#index"
・
・
end
```

以下を追記します。

```app/views/layouts/application.html.erb

<body>
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
・
・
</body>
```

Viewをインストールします。

```
$ rails g devise:views
```

以下が作られます。

app/views/devise/unlocks/new.html.erb
app/views/devise/shared/_links.html.erb
app/views/devise/shared/_error_messages.html.erb 
app/views/devise/sessions/new.html.erb
app/views/devise/registrations/new.html.erb
app/views/devise/registrations/edit.html.erb
app/views/devise/passwords/new.html.erb 
app/views/devise/passwords/edit.html.erb
app/views/devise/mailer/unlock_instructions.html.erb
app/views/devise/mailer/reset_password_instructions.html.erb
app/views/devise/mailer/password_change.html.erb
app/views/devise/mailer/email_changed.html.erb
app/views/devise/mailer/confirmation_instructions.html.erb
app/views/devise/confirmations/new.html.erb



モデルを作成します。
今回はUserモデルを作ります。


```
$ rails g devise user
```

以下モデルが作られます。

```app/models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
end
```

以下が同時に作られます。

```config/application.rb
config.i18n.default_locale = :ja
```

以下も作られます。

```config/routes.rb
ails.application.routes.draw do
devise_for :users
・
・
end
```

モデル作成後に、DBに反映します。

```
rails db:migrate
```


まだpage#indexのViewがないので作ります。

```
$ rails g controller Pages index
```

余談ですが、routes.rbにスコープを作ることでPathを変更できます。

```routes.rb
  devise_scope :user do
    get 'login', to: 'devise/sessions#new'
    post 'login', to: 'devise/sessions/#create'
    delete 'logout', to: 'devise/sessions#destroy' 
  end
```

これにより、
http://localhost:3000/login
にアクセスしたときに、ログインページが表示されます。

もちろんデフォルトの/users/sign_inでもログインページが表示されるので必要であれば削除しておきましょう。

```routes.rb
 devise_for :users, skip: [:sessions]
```



デバイスの設定は完了です。




##cancancanのインストール

次にcancancanをインストールしていきます。
これは認可と言って、ユーザによって、
アクセス権を付与するという意味になります。
システム管理者と一般ユーザの違いです。

```Gemfile
gem 'cancancan'
```

バンドルインストールします。

```
$bundle install
```
Abilityクラスが作成されます。

```
rails g cancan:ability
```

Abilityクラスのデフォルトです。

```
class Ability
  include CanCan::Ability

  def initialize(user)
    # Define abilities for the passed in user here. For example:
    #
    #   user ||= User.new # guest user (not logged in)
    #   if user.admin?
    #     can :manage, :all
    #   else
    #     can :read, :all
    #   end
    #
    # The first argument to `can` is the action you are giving the user
    # permission to do.
    # If you pass :manage it will apply to every action. Other common actions
    # here are :read, :create, :update and :destroy.
    #
    # The second argument is the resource the user can perform the action on.
    # If you pass :all it will apply to every resource. Otherwise pass a Ruby
    # class of the resource.
    #
    # The third argument is an optional hash of conditions to further filter the
    # objects.
    # For example, here the user can only update published articles.
    #
    #   can :update, Article, :published => true
    #
    # See the wiki for details:
    # https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities
  end
end
```
以下に変更します。

```
# frozen_string_literal: true

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new
    can :read,  :all

    if user.admin? 
      can :manage,  :all

    end



    # Define abilities for the passed in user here. For example:
    #
    #   user ||= User.new # guest user (not logged in)
    #   if user.admin?
    #     can :manage, :all
    #   else
    #     can :read, :all
    #   end
    #
    # The first argument to `can` is the action you are giving the user
    # permission to do.
    # If you pass :manage it will apply to every action. Other common actions
    # here are :read, :create, :update and :destroy.
    #
    # The second argument is the resource the user can perform the action on.
    # If you pass :all it will apply to every resource. Otherwise pass a Ruby
    # class of the resource.
    #
    # The third argument is an optional hash of conditions to further filter the
    # objects.
    # For example, here the user can only update published articles.
    #
    #   can :update, Article, :published => true
    #
    # See the wiki for details:
    # https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities
  end
end
```

user ||= User.new
上記がないとゲストユーザで操作しているときに、エラーになりました。

if user.admin? ←adminって何？と聞かれました。


 def initialize(user)
    user ||= User.new
    can :read,  :all
Abilityモデルが呼ばれたときに
初期値としてログインしていれば、そのユーザを変数に入れ、ログインしていなければゲストユーザが変数に代入されます。
そして、ユーザ変数に対して、read権限を付与するという意味となる。全てのユーザがread権限があるということです。

read権限というのは、CRUDに紐づいている訳ではないので注意しましょう。
ページが読めるということであり、getができるという意味ではないし、createメソッドとかも別にプログラムすればできます。

    if user.admin? 
      can :manage,  :all
こちらはUserモデルに作成したadminカラムにtrueが入っていれば実行するプログラムです。
つまりは管理者権限を持つユーザということになります。



Page#indexのビューページに以下を追加します。

```app/views/pages/index.html.erb
<h1>Pages#index</h1>
<p>Find me in app/views/pages/index.html.erb</p>
  <%= link_to "削除", logout_path, method: :delete %>

<% if can?  :update, current_user %>
  <h1>update</h1>

<% end %>

<% if can? :read, current_user %>
  <h1>read</h1>
<% end %>
```

<% if can?  :update, current_user %>
今ログインしているユーザがupdateの権限を持っていれば中身を表示します。

<% if can? :read, current_user %>
もしログインしているユーザがreadの権限を持っていれば中身を表示します。

という意味になります。

```db/schema.rb
  create_table "users", force: :cascade do |t|
    t.string "email", default: "", null: false
    t.string "encrypted_password", default: "", null: false
    t.string "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.boolean "admin", default: false
    t.index ["email"], name: "index_users_on_email", unique: true
    t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
  end
```

上記でadminってUserモデルにないけどどうするの？ってなるかと思うが、
追加することとなります。

```
$rails g migration AddAdminToUser admin:boolean
```

デフォルトで以下が作成されるので
default: "false"を追加します。

ビューページでは、adminを選択するビューは作成せず、デフォルトでユーザを作成したらadmin=falseがつくようにします。
ここでは、adminは管理側で意図的に作成することとします。

```db/migrate/20200522114428_add_admin_to_users.rb

class AddAdminToUsers < ActiveRecord::Migration[6.0]
  def change
    add_column :users, :admin, :boolean, default: "false"
  end
end
```

マイグレートします。


```
$rails g migrate
```


DBのスキーマの確認をします。

```db/schema.rb
 create_table "users", force: :cascade do |t|
    t.string "email", default: "", null: false
    t.string "encrypted_password", default: "", null: false
    t.string "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.boolean "admin", default: false
    t.index ["email"], name: "index_users_on_email", unique: true
    t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
  end
```

http://localhost:3000/
で一般ユーザを作成しておきます。

adminユーザは、
以下の通り作成します。

```
$user =User.new(id: xx, email: "xxx@yyy", password: "xxxx", admin: true)
```



これで手続きは完了なので、
実際に一般ユーザとAdminユーザそれぞれで、
Page#indexにアクセスします。

一般ユーザはread権限のみなのでreadを表示可能です。

Adminユーザは,manage権限（全ての権限を持つ）なのでread,updateそれぞれ表示可能です。