Railsチュートリアル第11章にて、メール認証によるアカウントの有効化について学んだので、その復習とメモ用に本記事を残そうと思います。
ここでは、すでにログイン機構(チュートリアル第8章)、およびRemember me機能(チュートリアル第9章)を実装済みの上で話を進めて行きます。
特にメソッドに関してはログイン機構で作成したものをそのまま使い回す、もしくは変更して使う場合が多いので、1から流れを理解したい方はログイン機構の作成から順に進めていくことをお勧めします。
開発環境
- Ruby 3.1.2
- Ruby on Rails 7.0.3
- M1 Macbook Air 2020
- mac OS Monterey (ver. 12.4)
- ターミナル bash (Rosetta 2 使用)
ソースコード
ユーザー認証機能(アカウント有効化)の仕上がりイメージ
今回実装するユーザー認証の、システム上の動作イメージは以下の通りです。
- ユーザーの初期状態は「有効化されていない」(unactivated)にしておく
- ユーザーが新規アカウント登録する(ユーザー名、メールアドレス、パスワードなどを入力し、送信する)
- 有効化トークン(activation_token)を生成する
- ③に対応する有効化ダイジェスト(activation_digest)を生成し、DBに保存する
- 有効化トークン(activation_token)とメールアドレスを含めた有効化リンクをメールに添付し、ユーザーに送信する
- ユーザーがメールを開き、有効化リンクをクリックする
- ⑥に該当するユーザーをDBから(メールアドレスをキーとして)検索し、保存された有効化ダイジェストと比較することで有効化トークンを認証する
- ユーザーの状態を「有効化済み」(activated)に変更する
こうすることで、メールアドレスによるユーザー認証を行うことができます。
実際にWebアプリケーション登録時に送信されるメールは以下のようなイメージです。
メールに記載のアカウント有効化リンクをクリックすることで、ユーザーの状態はactivated
となり、正式に登録されたとみなされます。
ユーザー認証機能(アカウント有効化)を実装する具体的な手順
まず最初に、ユーザー認証機能を実装する大まかな流れを以下に示します。
- AccountActivationsコントローラを生成する
- ハッシュ化したトークンを保存する有効化ダイジェスト
activation_digest
、および有効化の状態activated
、有効化の日時activated_at
を示すカラムを生成する - Userモデルにアカウント有効化のコードを追記する
- アカウント有効化メールのテンプレート(Userメイラー)作成
- 送信メールのプレビュー
- ユーザー登録時にアカウント有効化メールを送信する
- 他の認証でも使えるように
authenticated?
メソッドを一般化する - アカウントを有効化する(editアクションで有効化)
こんな感じです。
今回は基本的なユーザー認証機能(ログイン・ログアウト、Remember me)はすでにできており、それに機能追加する形で進めていきます。
それでは、それぞれの項目について詳しく見ていきましょう。
①AccountActivationsコントローラを生成する
UsersリソースやSessionsリソースのときと同様に、AccountActivationsリソースを作るためにはまずはAccountActivations
コントローラを生成します。
$ rails g controller AccountActivations
また、config/routes.rb
を以下のように設定します。
Rails.application.routes.draw do
root "home#index"
get "about", to: "home#about"
get "signup", to: "users#new"
get "login", to: "sessions#new"
post "login", to: "sessions#create"
delete "logout", to: "sessions#destroy"
resources :users, except: [:new]
resources :account_activations, only: [:edit]
end
②ハッシュ化したトークンを保存する有効化ダイジェスト(activation_digest)、および有効化の状態(activated)、有効化の日時(activated_at)を示すカラムを生成する
続いては、アカウントの有効化に必要なカラムを追加していきます。
今回必要なカラムは以下の3つです。
- 有効化ダイジェスト…「activation_digest」
- 有効化の状態…「activated」
- 有効化の日時…「activated_at」
追加するカラム名 | 型 |
---|---|
activation_digest | string |
activated | boolean |
activated_at | datetime |
これらを、以下のコマンドを実行して追加します。
$ rails g migration add_activation_to_users activation_digest:string activated:boolean activated_at:datetime
マイグレーションファイルが生成されるので、中身を開いて確認します。
class AddActivationToUsers < ActiveRecord::Migration[7.0]
def change
add_column :users, :activation_digest, :string
add_column :users, :activated, :boolean
add_column :users, :activated_at, :datetime
end
end
追加するカラム名などに間違いがなければ、マイグレーションを実行してカラムを追加します。
$ rails db:migrate
③Userモデルにアカウント有効化のコードを追記する
まずは、トークンを一時保存しておくための仮装属性をUserモデル内に定義しておきます。
attr_accessor :activation_token
これで、activation_token属性へのアクセス(読み書き)ができるようになりました。
次に、生成したトークンをactivation_token属性に代入すると同時に、ハッシュかしてDB内に保存するためのメソッドをprivateメソッドとして定義します。
・・・
private
# 有効化トークンとダイジェストを作成及び代入する
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end
上記のメソッドをオブジェクトの作成(create)時に呼び出すようにするため、before_createコールバックを使って以下のように記述します。
(新規ユーザー登録時にこのメソッドが呼び出される)
before_create :create_activation_digest
最終的に、user.rb
に記述する内容は以下のようになります。
class User < ApplicationRecord
attr_accessor :remember_token, :activation_token
before_save :downcase_email
before_create :create_activation_digest
・・・
private
# メールアドレスを全て小文字にする
def downcase_email
self.email = email.downcase
end
# 有効化トークンとダイジェストを作成及び代入する
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end
④アカウント有効化メールのテンプレート(Userメイラー)作成
モデルやコンローラーと同様に、メイラーも以下のコマンドで生成できます。
$ rails g mailer UserMailer account_activation
続いて、作成したUserメイラーuser_mailer.rb
を以下のように編集します。
class UserMailer < ApplicationMailer
def account_activation(user)
@user = user
mail to: user.email, subject: "Account activation"
end
end
ここでは送信メール本文にユーザー名を含め、カスタムの有効化リンクを追加します。
この後、DBからユーザーをメールアドレスで検索して有効化トークンを認証できるようにするために、リンクには有効化トークンとメールアドレスを両方含めておく必要があります。
ここで、以下のようなURLパスを指定するメソッドについて思い出してみましょう。
edit_user_url(user)
これは、usersコントローラーのeditアクションへのパス(userを引数)を表していますね。
上記のメソッドは、次の形式のURLを生成します。
http://localhost:3000/users/1/edit
これと同様に、account_activationsコントローラーのeditアクションへのパス(@userの有効化トークンを引数)を指定する場合は、
edit_account_activation_url(@user.activation_token)
これに対応するアカウント有効化リンクのベースURLは以下のようになります。
http://localhost:3000/account_activations/X9FkWNOAQru914FbR-rdAQ/edit
上記URLには有効化トークンX9FkWNOAQru914FbR-rdAQ
(Users.new_tokenメソッドで自動生成)が含まれていることがわかります。
(この有効化トークン、先ほどの...users/1/edit
のid=1
に相当すると考えれば分かりやすいと思います)
今回はベースURLにメールアドレスも含ませたいので、有効化リンクのパス(有効化トークンとメールアドレスを両方含んだURL)は以下のように表されます。
edit_account_activation_url(@user.activation_token, email: @user.email)
メールアドレスはクリエパラメータ(URLの末尾で疑問符「?」に続けてキーと値のペアを記述したもの)を使うことで有効化リンク(ベースURL)に組み込むことができます。
最終的に、有効化リンクのURLは以下のように表されます。
(メールアドレスはsample@example.com
としています)
http://localhost:3000/account_activations/X9FkWNOAQru914FbR-rdAQ/edit?email=sample%40example.com
ここで、%40
となっているのは、@
などの特殊記号はURLでは扱えないため、自動的にエスケープされたためです。
(後でparams[:email]でメールアドレスを読み込むときにエスケープは自動的に解除されます。)
それでは、上記の内容を踏まえてメールのテンプレートを作成していきましょう。
Userメイラー追加時にapp/views/user_mailer
ディレクトリ内に自動生成されたテンプレート(html用、text用)を以下のように編集します(文章は各自好きな文言を入れてください)。
<h1>User Authentication App</h1>
<p>こんにちは、<%= @user.name %>さん。</p>
<p>
この度は◯◯Webサービスに仮登録いただきありがとうございます。
</p>
<p>
つきましては、下記リンクをクリックしてアカウントを有効化してください。
</p>
<%= link_to "アカウントを有効化する", edit_account_activation_url(@user.activation_token, email: @user.email) %>
こんにちは、<%= @user.name %>さん。
この度は◯◯Webサービスに仮登録いただきありがとうございます。
つきましては、下記リンクをクリックしてアカウントを有効化してください。
<%= edit_account_activation_url(@user.activation_token, email: @user.email) %>
テンプレートはこんな感じでOKです。
⑤送信メールのプレビュー
送信するメールがどのような感じになるのか、あらかじめ確認しておきたい場合に便利なのがメールのプレビュー機能です。
Railsのメール送信のプレビューでは、HTMLメール、Textメールの両方のバージョンのプレビューを確認することができます。
ここでは、このプレビュー機能を利用するための設定をしていきます。
まず、config/environments/development.rb
を以下のように設定します。
Rails.application.configure do
・・・
host = 'localhost:3000'
config.action_mailer.default_url_options = { host: host, protocol: 'http' }
・・・
end
上記の環境設定は、お手持ちのPC(OS)の環境によって異なる場合があるので注意。大体は上記の設定でOKなはずです。
続いては、Userメイラー生成時に自動生成されたプレビューファイルの設定をしていきます。
test/mailers/previews
ディレクトリに自動生成されたuser_mailer_preview.rb
を以下のように編集します。
class UserMailerPreview < ActionMailer::Preview
# Preview this email at http://localhost:3000/rails/mailers/user_mailer/account_activation
def account_activation
user = User.first
user.activation_token = User.new_token
UserMailer.account_activation(user)
end
end
ここで、コード中にコメントアウトされた状態で記載されている以下のようなURL
http://localhost:3000/rails/mailers/user_mailer/account_activation
をブラウザのアドレスバーに入力すると、送信メールのプレビューを表示することができます。(railsサーバーが起動した状態で)
⑥新規ユーザー登録時にアカウント有効化メールを送信する
usersコントローラーのcreateアクションに以下のように記述します。
class UsersController < ApplicationController
・・・
def create
@user = User.new(user_params)
if @user.save
UserMailer.account_activation(@user).deliver_now
flash[:notice] = "アカウント有効化メールを送信しました。メールが届きましたら、記載されているリンクをクリックしてアカウントを有効化してください。"
redirect_to root_url
else
render :new, status: :unprocessable_entity
end
end
・・・
end
ここで、メールを送信する処理を行う以下のコード、
UserMailer.account_activation(@user).deliver_now
これが何をしているのかというと、
Userメイラーのaccount_activation(user)メソッドを実行してテンプレートを呼び出し、引数として指定した@user宛てにdeliver_nowで今すぐメールを送信する、
といった処理をしています(実際にメールを送信するためには、別途メールサーバーの設定が必要です)。
class UserMailer < ApplicationMailer
def account_activation(user)
@user = user
mail to: user.email, subject: "Account activation"
end
end
ここで、メールを送信する一連の処理をメソッド化してuser.rb
内に記述しておきます。
class User < ApplicationRecord
・・・
# 有効化用のメールを送信する
def send_activation_email
UserMailer.account_activation(self).deliver_now
end
private
・・・
end
こうすることで、メール送信時のコードを以下のようにシンプルにすることができます。
@user.send_activation_email
usersコントローラーのcreateアクションに記述する、最終的なコードは以下の通りです。
class UsersController < ApplicationController
・・・
def create
@user = User.new(user_params)
if @user.save
# UserMailer.account_activation(@user).deliver_now を下記のように変更
@user.send_activation_email
flash[:notice] = "アカウント有効化メールを送信しました。メールが届きましたら、記載されているリンクをクリックしてアカウントを有効化してください。"
redirect_to root_url
else
render :new, status: :unprocessable_entity
end
end
・・・
end
⑦他の認証でも使えるようにauthenticated?
メソッドを一般化する
アプリケーションから有効化リンクをメール送信できる準備が整ったら、次はメールに記載された有効化リンクをクリックした時の認証を行うメソッドを準備します。
有効化リンクをクリックした時の認証は、次のようなコードで行うことにします。
user = User.find_by(email: params[:email])
if user && user.authenticated?(:activation, params[:id])
認証の大まかな流れとしては、
- 有効化リンクに埋め込まれたメールアドレスからDB内のユーザーを検索
- 該当するユーザーの有効化ダイジェスト(ハッシュ化されたトークン)と、有効化リンクに埋め込まれたトークンが一致するか比較
- 一致したらtrueを返し、アカウントを有効化する処理へ進む
といった感じです。
このトークンの認証はチュートリアル第9章の「Remember me」機能の実装で以下のように定義してありました。
# トークンがダイジェストと一致したらtrueを返す
def authenticated?(remember_token)
return false if remember_digest.nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
上記のメソッドとは別に、アカウント有効化用のauthenticated?メソッドを別途定義しても良いのですが、RailsのDRYにのっとり極力コード量を減らしたいので使いまわせる形にするのが理想です。
(remember_token, remember_digest の、rememberの部分をactivationという文字列に置き換えたい)
そこで、今回はsendメソッドという魔法のようなメソッドを使い、他の認証時にも使いまわせるようauthenticated?メソッドを一般化したいと思います。
sendメソッドを用いることで、受け取ったパラメータに応じて呼び出すメソッドを切り替えることができるようになります。
以下、DB内の最初のユーザーが持つactivation_digest
属性にアクセスする例を見てみましょう。
irb(main):001:0> user = User.first
irb(main):003:0> user.activation_digest
=> "$2a$12$e537GqkvOZ3qNcefV5AF8.U3.IhLzBc3NGSQDpc/a9XYrjHP0P6ra"
irb(main):004:0> user.send(:activation_digest)
=> "$2a$12$e537GqkvOZ3qNcefV5AF8.U3.IhLzBc3NGSQDpc/a9XYrjHP0P6ra"
irb(main):005:0> user.send("activation_digest")
=> "$2a$12$e537GqkvOZ3qNcefV5AF8.U3.IhLzBc3NGSQDpc/a9XYrjHP0P6ra"
irb(main):006:0> attribute = :activation
=> :activation
irb(main):008:0> user.send("#{attribute}_digest")
=> "$2a$12$e537GqkvOZ3qNcefV5AF8.U3.IhLzBc3NGSQDpc/a9XYrjHP0P6ra"
ここで、
> user.activation_digest
=> “$2a$12$e537GqkvOZ3qNcefV5AF8.U3.IhLzBc3NGSQDpc/a9XYrjHP0P6ra”
> user.send(“#{attribute}_digest”)
=> “$2a$12$e537GqkvOZ3qNcefV5AF8.U3.IhLzBc3NGSQDpc/a9XYrjHP0P6ra”
この両者が等価である点に注目です。
後者の例では、シンボル:activation
と等しいattribute
変数を定義し、文字列の式展開(interpolation)を使って引数を正しく組み立ててから、send
に渡しています。
つまり、sendメソッドを使うことで、同じauthenticated?
メソッドでも、
attribute変数の部分を(:remember や :activation などに)入れ替えるだけで、認証に応じたメソッドを自由に切り替えることができるのです。
このsendメソッドの性質を利用してremember me 機能実装時に定義したauthenticated?メソッドを書き換えてみましょう。
def authenticated?(remember_token)
digest = self.send("remember_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(remember_token)
end
上のコードの各引数を一般化し、文字列の式展開も利用すると、次のようなコードに置き換えることができます。
# 一般化されたauthenticated?メソッド
# Userモデル内では「self.」は省略可
def authenticated?(attribute, token)
digest = self.send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
このコードをUserモデルuser.rb
に記述します。
Userモデル内では「self.」の記述は省略できるので、最終的には以下のように記述できます。
class User < ApplicationRecord
・・・
# 渡されたトークンがダイジェストと一致したらtrueを返す
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
・・・
end
ここで、sessions_helper.rb
のcurrent_user
メソッドのauthenticated?
が古いままで、引数が2つではなくまだ1つのままになっているので忘れないうちに変更しておきます。
・・・
# 変更前のコード
def current_user
if (user_id = session[:user_id])
@current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.encrypted[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(cookies[:remember_token])
log_in user
@current_user = user
end
end
end
# ↓↓↓↓↓ 以下のようにコードを変更
def current_user
if (user_id = session[:user_id])
@current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.encrypted[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(:remember, cookies[:remember_token])
log_in user
@current_user = user
end
end
end
・・・
end
⑧アカウントを有効化する(editアクションで有効化)
ユーザー新規登録(仮登録)時にご自身のメールアドレス宛に以下のような有効化リンクが送られてきたとします。
http://localhost:3000/account_activations/X9FkWNOAQru914FbR-rdAQ/edit?email=sample%40example.com
この有効化リンクをクリックすると、どのようにアカウントが有効化されるのでしょうか。
上記の有効化リンク、ちょっと複雑そうに見えますが、基本構造は以下のようなパターンに似ています。
http://localhost:3000/users/1/edit
これは、usersコントローラーの(ユーザーidが1の)editアクションを実行するURLですね。
ここで、usersコントローラー内でparams[:id]を使うことでユーザーid(1)を取得することができます。
では、今回の有効化リンクに当てはめて見ましょう。
http://localhost:3000/account_activations/X9FkWNOAQru914FbR-rdAQ/edit
これは、account_activationsコントローラーの(トークンidがX9FkWNOAQru914FbR-rdAQの)editアクションを実行するURLですね。
こちらも同様に、account_activationsコントローラー内でparams[:id]を使うことで有効化トークン(X9FkWNOAQru914FbR-rdAQ)を取得することができます。
このURLにクリエパラメータを使ってメールアドレスを組み込んだものが以下のような有効化リンクになります。
http://localhost:3000/account_activations/X9FkWNOAQru914FbR-rdAQ/edit?email=sample%40example.com
(URLにメールアドレスを組み込む際に、特殊記号「@」は自動的にエスケープされます)
ちなみに、組み込んだメールアドレスも同様にparams[:email]を使うことで取り出すことができます(エスケープは読み込み時に自動的に解除される)。
以上の内容を踏まえて、この有効化リンクをクリックした時にアカウントを有効化するためには、
リンクへアクセス時にparams[:email]とparams[:id]から有効化トークンとメールアドレスを取得して認証するよう、account_activationsコントローラーのeditアクションに記述します。
具体的な流れは以下の通り。
params[:email]
でemail=sample%40example.com
を取得(%40は自動的にエスケープ解除される)し、該当するユーザーをDBから検索params[:id]
で有効化トークンX9FkWNOAQru914FbR-rdAQ
を取得- 取得したトークンとDBにハッシュ化して保存されたトークン(該当するユーザーのもの)を
authenticated?
メソッドで比較 - trueの場合はアカウントを有効化する(falseの場合は有効化せずにroot_urlにリダイレクト)
これらの一連の処理の流れを、account_activationsコントローラーに以下のように記述します。
class AccountActivationsController < ApplicationController
def edit
user = User.find_by(email: params[:email])
if user && !user.activated? && user.authenticated?(:activation, params[:id])
user.activate
log_in user
flash[:notice] = "アカウントが有効化されました"
redirect_to user
else
flash[:error] = "この有効化リンクは無効です"
redirect_to root_url
end
end
end
アカウントを有効化するactivate
メソッドは別途user.rb
内に定義します。
class User < ApplicationRecord
・・・
# アカウントを有効にする
def activate
update_attribute(:activated, true)
update_attribute(:activated_at, Time.zone.now)
end
private
・・・
end
ここで、
update_attribute(:activated, true)
とすることで、アカウントを有効化しています。(デフォルトではfalse
に設定されている)
これで、アカウントの有効化ができるようになりました。
ただ、今のままだと有効化されていないユーザー(:activated
がfalse
のユーザー)までログインできてしまうので、
有効化されていないユーザーがログインできないようにするよう、sessionsコントローラーのcreateアクションにて、
if user.activated?
で条件分岐し、有効化済みのユーザーのみログインできるようにします。
class SessionsController < ApplicationController
・・・
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
if user.activated?
log_in(user)
params[:session][:remember_me] == "1" ? remember(user) : forget(user)
flash[:notice] = "ログインに成功しました"
redirect_to user
else
message = "アカウントが有効化されていません。"
message += "届いたメールをご確認の上、アカウントを有効化してください。"
flash[:warning] = message
redirect_to root_url
end
else
flash.now[:error] = "ログインに失敗しました"
render "new", status: :unprocessable_entity
end
end
・・・
end
これで今回実装したかったユーザー認証(アカウント有効化)は完成です。
【追記】有効化(認証)リンクに有効期限を設定する
アカウント有効化の実装はできましたが、このままだとユーザーがアカウント認証を済ませるまでメールのリンクは(半永久的に)有効となります。
つまり、もし万が一アカウントを有効化する前に認証メールが第三者に渡ってしまった場合、その第三者が認証を済ませてアカウントを不正利用してしまうかもしれません。
そこで、この認証メールの有効化リンクに有効期限を持たせることで、上記のリスクを極力回避するようにします。
有効化リンクに有効期限を持たせるためには、その有効化リンクが送信された日時の情報が必要になるので、
まずはusersテーブルに有効化リンクの送信日時を示すカラムactivation_sent_at
を追加します。
$ rails g migration add_activation_sent_at_to_users activation_sent_at:datetime
マイグレーションファイルが生成されるので、中身を開いて確認します。
class AddActivationSentAtToUsers < ActiveRecord::Migration[7.0]
def change
add_column :users, :activation_sent_at, :datetime
end
end
追加するカラム名などに間違いがなければ、マイグレーションを実行してカラムを追加します。
$ rails db:migrate
続いて、user.rb
にメール送信時にactivation_sent_at
に現在時刻を更新するメソッド、および有効化リンクの有効期限(今回は24時間に設定)を設定するメソッドをそれぞれ記述します。
・・・
# 有効化用のメールを送信する
def send_activation_email
UserMailer.account_activation(self).deliver_now
#-------- 追記(メール送信時の日時を更新する) ---------
update_attribute(:activation_sent_at, Time.zone.now)
#------------------------------------------------
end
・・・
#---------------- 追記 --------------------
# 有効化リンクの有効期限(24時間)が切れている場合はtrueを返す
def activation_expired?
activation_sent_at < 24.hours.ago
end
#------------------------------------------
private
・・・
最後に、account_activations_controller.rb
を以下のように編集します。
class AccountActivationsController < ApplicationController
def edit
@user = User.find_by(email: params[:email])
# --------- !@user.activation_expired? を追記 ---------------
if @user && !@user.activated? && !@user.activation_expired? && @user.authenticated?(:activation, params[:id])
# ----------------------------------------------------------
@user.activate
log_in @user
flash[:notice] = "アカウントが有効化されました"
redirect_to @user
else
flash[:error] = "このリンクは無効、もしくは有効期限切れです"
# ------- 追記 --------
@user.destroy if @user
# --------------------
redirect_to signup_url
end
end
end
ここで、else
以下の@user.destroy if @user
とすることで、アカウント認証に失敗した場合はデータベース内から仮登録したユーザー情報を削除するようにしています。
もし、認証に失敗したのに仮登録したユーザーがデータベースに残ったままだと、再登録しようとしても同じメールアドレスで登録できなくなってしまうからです。
本番環境でのメール送信(SendGrid)
アカウントの有効化機能の実装ができたら、実際に本番環境でメールが送信できるか確認してみましょう。
本番環境(および開発環境)におけるメールサーバーの設定方法は下記記事を参考にしてみてください。
新規ユーザー登録時に、以下のようなアカウント有効化メールが届くかと思います。
(Gメールアドレス宛てに届いたhtmlメールの例です。メールサーバーをSendGridにした場合、送信元に以下のように「sendgrid.net 経由」と記載されているかと思います)
アカウント有効化リンクをクリックして、問題なければOKです。
以上です。
お疲れ様でした。
コメント