Railsチュートリアル 10章を終えました。
10章ではユーザ情報更新画面を作成していきます。
Progate Railsコースでもユーザ情報画面は実装しますが、
ここでは(認証 (authentication) (・・・ユーザーが実行可能な操作を管理すること)も考慮します
ユーザ更新
ユーザ更新画面
ユーザがユーザ情報を編集できるように、ユーザ情報編集画面を作っていきます。
users/id/edit
にアクセスした際に、ユーザ情報が表示されるように
コントローラを設定していきます。
具体的にはユーザテーブルにidを検索条件に引っ張ってきます
def edit @user = User.find(params[:id]) end
更新
save change
ボタンを押下することでユーザ更新できるようにします。
'edit.html.erb'からusers_controller.rb
のupdateメソッドをアクセスし、
ユーザ更新成功、失敗時の処理を書いていきます。
app/view/users/edit.html.erb
<div class="gravatar_edit"> <%= gravatar_for @user %> <a href="http://gravatar.com/emails" target="_blank" rel="noopener">Change</a> </div>
app/controllers/users_controller.rb
def update @user = User.find(params[:id]) if @user.update_attributes(user_params) flash[:success] = "Profile updated" redirect_to @user else render 'edit' end end
ユーザ一覧と削除
次にユーザ一覧画面とその画面からユーザ削除する機能を作っていきます。
ページネーション
とはいえもしユーザ数が数百単位、数千単位になったときに一つの画面に全部表示されたら、
表示がかなり重たくなったり、使い勝手が悪くなったりします
ページネーションを使うことで数人単位で表示するようにします
ページネーションを自分で実装する場合、全ユーザー数を把握して、一ページ毎の表示数から何ページを作成するかを計算し、ページリンクを作成したり、最終ページが全てのユーザの表示数が表示されるとは限らないので・・・
ということを計算しないといけないです
幸いにもRailsにはそういうことを自動でやってくれるwill_paginate gem
とbootstrap-will_paginate
が開発されています
なのでこれを導入しながらページネーションを作成していきます
Gemfile
gem 'will_paginate' gem 'bootstrap-will_paginate'
Gemfileに上記を書き込んだら、$ bundle install
でinstallしていきます
管理者権限追加
ユーザ削除機能が全てのユーザに権限があるのは不都合です。
そこでUserテーブルにadminというカラムをbool型で作成します。
$rails generate migration add_admin_to_users admin:boolean
マイグレーションファイルに:boolean, default: false
を追加することによって
adminの初期値にfalseに設定することができます
class AddAdminToUsers < ActiveRecord::Migration[5.0] def change add_column :users, :admin, :boolean, default: false end end
あとはマイグレを行います
$ rails db:migrate
ユーザ一覧
次にユーザ一覧と削除を追加していきます
app/view/users/index.html.erb
<% provide(:title, 'All users') %> <h1>All users</h1> <%= will_paginate %> <ul class="users"> <%= render @users %> </ul> <%= will_paginate %>
ここでwill_paginateで下記のようなページネーションを追加することができます
また、<%= render @users %>
は自動的にユーザーのコレクションを列挙し、それぞれのユーザーを_user.html.erbパーシャルで出力します。
ということで出力先のapp/view/users/_user.html.erb
を書いていきます
<li> <%= gravatar_for user, size: 50 %> <%= link_to user.name, user %> <% if current_user.admin? && !current_user?(user) %> | <%= link_to "delete", user, method: :delete, data: { confirm: "You sure?" } %> <% end %> </li>
ここで削除時のリンクを張っていますが、ログインしているユーザが管理者である場合のみ表示します(詳しくは後述します)
次に一覧と削除のコントローラですpaginate
を使うことでページ毎のユーザを取得することができます
app/controllers/users_controller.rb
def index @users = User.paginate(page: params[:page]) end def destroy User.find(params[:id]).destroy flash[:success] = "User deleted" redirect_to users_url end
認可
これでユーザ編集とユーザ削除が行えますが、このままではユーザ編集についてはURLを直接叩くことでログインユーザ以外のユーザも編集可能になってしまいます
これを回避するために、ログインしているユーザのみに編集可能し、管理者権限のあるユーザのみにユーザ削除するようにします
ログイン必須にする
ログインしていないユーザーが保護されたページにアクセスしようとした際、ログインページに転送するようにします
before_action
を使用すると、処理の前に実行できるメソッドを定義できます
app/controllers/users_controller.rb
before_action :logged_in_user, only: [:index, :edit, :update, :destroy] # ログイン済みユーザーかどうか確認 def logged_in_user unless logged_in? store_location flash[:danger] = "Please log in." redirect_to login_url end end
app/helper/SessionsHelper.rb
# アクセスしようとしたURLを覚えておく def store_location session[:forwarding_url] = request.original_url if request.get? end
編集、一覧、削除、更新を行う前にログイン済みかどうかを確認し、ログインしていない場合、ログイン画面に転送しメッセージを表示します
また、store_locationメソッドではリクエストが送られたURLを覚えておき、ログインしていないユーザーが編集ページにアクセスしようとしていたなら、ユーザーがログインした後にはその編集ページにリダイレクトされるようにします。
正しいユーザーを要求する
このままだと、ログインすれば他のユーザまで編集ができてしまいます
これを回避するために、編集作業の前に、ログインしたユーザが自ユーザかどうかを判定します
app/controllers/users_controller.rb
before_action :correct_user, only: [:edit, :update] # 正しいユーザーかどうか確認 def correct_user @user = User.find(params[:id]) redirect_to(root_url) unless current_user?(@user) end
app/helper/SessionsHelper.rb
# 与えられたユーザーがログイン済みユーザーであればtrueを返す def current_user?(user) user == current_user end
これでログイン済みかつ正しいユーザーのみ編集作業や更新作業ができます。
削除時の制限
削除時にもコマンドラインからDELETEリクエストを送ることで全ユーザを削除されてしまうことができるようです
ある程度の腕前を持つ攻撃者なら、コマンドラインでDELETEリクエストを直接発行するという方法でサイトの全ユーザーを削除してしまうことができるでしょう。サイトを正しく防衛するには、destroyアクションにもアクセス制御を行う必要があります。
これを保護するために、削除前に削除者が管理者かどうかを調べます。
before_action :admin_user, only: :destroy # 管理者かどうか確認 def admin_user redirect_to(root_url) unless current_user.admin? end
管理者権限を更新制限
ユーザモデルに管理者属性を追加しましたが
ユーザに管理権限を意図せず勝手に追加することもPATCHコマンドから可能になってしまいます
patch /users/17?admin=1
上記コマンドでid=17のユーザが勝手にadmin権限が付与されますので、
編集してもよい安全な属性だけを更新することにします
app/controllers/users_controller.rb
def create @user = User.new(user_params) ・・・ end def update @user = User.find(params[:id]) if @user.update_attributes(user_params) ・・・ end def user_params params.require(:user).permit(:name, :email, :password, :password_confirmation) end
上記user_paramsメソッドで、管理者以外の属性を編集可能にしています。これにより、任意のユーザーが自分自身にアプリケーションの管理者権限を与えることを防止できます。
最後に
10章のユーザ一覧、編集、削除はprogateのRailsコースでもありましたが、認可については踏み込んだ内容となりました
またそれ以外にも基本的に使いそうな機能も記載がありましたので、何回か見る章になりそうですね
参照
下記のblog様は本当に文章がうまいため、記事にする際にもかなり参考にさせていただいております。
ありがとうございます