[初心者向け] Railsで管理ページを作成し、管理者権限を持つユーザーのみアクセス可能にする[rails_admin]

#はじめに
右も左もわからないプログラミング初心者が書く備忘録です。
膨大な勉強量の中で気になった事をメモしていきます。

今回ポートフォリオの作成ともあり、管理者ページに転移する際にパスワードを求めたくない
（担当の方に余計な一手を打たせたくない）という理由からはじめました
しかし、普通に運用する際もセキュリティの向上が期待できると思いますので
パスワードのみでブロックしている方などは参考にしてみてください♪

#今回の内容
gem 'devise'
gem 'cancancan'
gem 'rails_admin', '~> 2.0'
を使用し、webアプリに管理者ページを作成したいと思います


管理用ページのアクセスに関して

・アクセス可能なのは管理者のみ
・他のユーザーはアクセス不可（トップページへ飛ばす）
・ログインしていない場合、ログインページへ移動させる

を意識していきたいと思います

#開発環境
Rails 6.1.3.2
ruby 2.5.1
Docker version 20.10.6
devise (4.8.0)
cancancan (3.3.0)
rails_admin (2.2.1)
> RailsAdmin 2.0.1、2.0.0、および1.4.2までは、XSSの脆弱性があると報告されています。

#事前準備
今回、DeviseによるuserのCRUDは出来ているものとします
まだの方は導入が終わってからお進みください♪
以下記事がSNS認証を含めていますが、とても参考になると思います！

https://qiita.com/cigalecigales/items/16ce0a9a7e79b9c3974e

---

deviseの導入が終わったら、まずは管理者権限を付与するためにUserテーブルにadminを追加します

```:ターミナル
$ rails g migration AddAdminToUsers
```

上記コマンドで出来たマイグレーションファイルを編集します

```rb:db/migrate
class AddAdminToUsers < ActiveRecord::Migration
  def change
    add_column :users, :admin, :boolean, default: false
  end
end
```

```:ターミナル
$ rails db:migrate
```

これでadminという真偽値を持つカラムができました

これを利用し
true  => 管理者権限あり
false => 一般ユーザー
と区分けをする事ができます♪

ユーザーが新規作成された時はfalseが入る（一般ユーザーに該当する）ようになっています

既に権限を付与したいユーザーが存在する場合
以下コマンドで管理者権限を付与してください

```:ターミナル
$ rails c

$ User.all
  ここで権限を付与したいユーザーのIDを確認します

$ user = User.find(権限を付与したいユーザーのID)
  userという変数に権限を付与したいユーザーが入ります

$ user.admin = true
  ユーザーのadminをtrueにして管理者権限を付与します

$ user.save
　　　設定を保存します
```

次にGemの追加です


```rb:Gemfile
gem 'cancancan'
gem 'rails_admin', '~> 2.0'
```

```:ターミナル
$ bundle install
```

ここからは順を追って説明します

#rails_admin
とてもわかり易いので一読してみてください♪

https://github.com/sferik/rails_admin

```:ターミナル
$ rails g rails_admin:install
Running via Spring preloader in process 23
           ?  Where do you want to mount rails_admin? Press <enter> for [admin] > 
       route  mount RailsAdmin::Engine => '/admin', as: 'rails_admin'
      create  config/initializers/rails_admin.rb
```

環境により、名前を付けてくれと言われるかもしれません

```:ターミナル
# この文章です
?  Where do you want to mount rails_admin? Press <enter> for [admin] > 
```
変える必要がなければ、そのままEnterを押してください

これだけで、トップページのURLに /admin をつけてあげれば管理用ページに入れます
とても簡単ですよね♪

まずは簡単な設定を行います（コメントアウトを外すだけ）

```rb:config/initializers/rails_admin.rb
  ## == Devise ==
  config.authenticate_with do
    warden.authenticate! scope: :user
  end
  config.current_user_method(&:current_user)

  ## == CancanCan ==
  config.authorize_with :cancancan
```

###後ほど、重要な設定を行いますので気に留めておいてください

#cancancan
管理者以外をトップページに飛ばすという工程を担当してもらうために導入します
最初はrails_adminのコントローラーの名前空間を利用して
redirectさせれば良いかと思っていたのですが、なかなかに手間なのでcancancanを使用します

使いこなせばとても便利なGemですので調べてみてください♪

こちらもとてもわかり易いので御一読を

https://github.com/CanCanCommunity/cancancan

```:ターミナル
$ rails g cancan:ability
Running via Spring preloader in process 21
      create  app/models/ability.rb
```

上記コマンドで出来たファイルを修正します
コメントアウトされている箇所は、丁寧に説明してくれている文なので
翻訳して見ておきましょう♪
（確認後、私は削除しました）

```rb:models/ability.rb
class Ability
  include CanCan::Ability

  def initialize(user)
    if user && user.try(:admin?)
      can :access, :rails_admin
      can :manage, :all
    end
  end
end
```

#テストしてみる
１.ログインせずに管理者ページへ
　=>deviseの効果でログイン画面へ

2.管理者権限を付与したユーザーで管理ページへ
　=>管理ページへ転移

3.権限のないユーザーで管理ページへ
　=>エラー！！！

という結果になると思います

#エラー内容

権限を付与していないのであたりまえなのですが、このページにアクセスする権限がありませんとのこと
![スクリーンショット 2021-08-25 18.08.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1624943/64919e6b-4fe4-bd86-9da4-29997aff204c.png)

ですがエラーを出すのではなく、トップページへ転移して欲しい！！！

#修正箇所
今回のキモとなるコードです

###config.parent_controller = 'ApplicationController'

この記載がないと、application_controller.rbにコードを書いても読まれません

```rb:config/initializers/rails_admin.rb
  config.parent_controller = 'ApplicationController' 　# これを追加

# 以下はそのまま
  ### Popular gems integration

  ## == Devise ==
  config.authenticate_with do
    warden.authenticate! scope: :user
  end
  config.current_user_method(&:current_user)

  ## == CancanCan ==
  config.authorize_with :cancancan
```

```rb:controllers/application_controller.rb
  rescue_from CanCan::AccessDenied do |exception|
    respond_to do |format|
      format.json { head :forbidden }
      format.html { redirect_to main_app.root_path, alert: "管理者権限がないのでアクセスできません" }
    end
  end
```

この記述をしてあげれば、エラーがなくなり
権限を持たないユーザーが管理用ページに入ろうとした時にトップページへ転移させることができます

" alert: "管理者権限がないのでアクセスできません" "

の部分は好きな文言に変えて使用してください

また、viewファイルでは

```erb:views/html.erb
<% if user_signed_in? && current_user.admin == true %>
<!-- 管理者にのみ表示したい内容例 -->
　　　　<%= link_to '/admin' do %>
    <button type="button" class="btn btn-outline-danger admin-btn">管理者用ページはこちら！</button>
  <% end %>
<% end %>
```

などで、管理者のみ管理用ページへのリンクを表示させるなどしておきましょう！

# rails_adminを日本語化する
現状、管理用ページの一番上に
”日本語になってないよ！”
と記載されていると思います。

実際にページ内は英語で書かれていると思いますが
管理する際、適宜日本語化しておいたほうが気持ちいいので変えていきます

今回はこの方のコードをお借りしました

https://gist.github.com/mshibuya/1662352

今回はわかりやすくするため、config/locales配下に専用のファイルを作りました

```rb:config/locales/rails_admin.ja.yml
コピーしたコードを貼り付けてください
```

モデル名なども日本語化したければ追加で記述してあげれば大丈夫だと思います

そして　gem 'rails-i18n'　を導入していないならここで入れておきましょう

```rb:Gemfile
gem 'rails-i18n'
```

```:ターミナル
$ bundle install
```

config/apprication.rbに以下を記述

```rb:config/apprication.rb
module App
  class Application < Rails::Application
               ・
              省略
               ・
    config.i18n.default_locale = :ja
               ・
              省略
               ・
  end
end
```

以上で管理用ページが実装出来たと思います:grinning:

今回はあまり時間がなく、ぱぱっと書いてしまいましたので
間違いなど御座いましたらコメントして頂けると幸いです！

#最後に
勉強を始めたばかりで知識もなく、拙い文章ですがアウトプットすることで頭の中を整理しつつ、どなたかのお役に立てれば良いなと思い投稿させて頂きました。
最後まで見て頂きありがとうございました！