はじめに
Railsで認証システムを作るときに便利なGemとして devise
が挙げられます。
https://github.com/plataformatec/devise
deviseは認証システムを作るツールとしては強力であり、カスタマイズなしでも高度な機能が一通り揃っています。
しかし、いざカスタマイズして使おうとすると色々と考えなくてはいけないこともあります。
今日は、ユーザ登録を行なってからすぐにログインはできないようにし、管理者が承認してから初めてログインできるようなプロセスで運用したいときに気をつけておかなければならないことを共有します。
ユーザー登録後に管理者の承認プロセスが踏んでからログインできるようにするには
deviseのwikiに以下のようなものがありました。
その中でも特に、 ActiveRecordのメソッドを使っている部分に注目します。
after_create :send_admin_mail
def send_admin_mail
AdminMailer.new_user_waiting_for_approval(email).deliver
end
<p><%= @email %> has registered to join your site!</p>
<p>An admin can approve this registration by visiting the website and editing the user</p>
ユーザー作成後にメールを飛ばすメソッドが走っています。これを参考に、ユーザーテーブルに、承認フラグのカラムを追加して、管理者が承認した場合、アカウントが承認された旨を伝えるメソッドを追加しました。
- 管理者画面や承認プロセスを走らせるメソッド関連は省略
+ after_update :send_approved
+ def send_approved
+ AdminMailer.user_approved(email).deliver
+ end
after_update だけだとサインイン時にもメールが飛んでしまう
しかし、これには致命的な問題があります。
deviseでサインインをした場合、DBではサインインした回数やユーザがアクセスして来たIPアドレスの情報などのデータのupdateが走ってしまいます。
User Update (2.3ms) UPDATE "users" SET "current_sign_in_at" = $1, "last_sign_in_at" = $2, "current_sign_in_ip" = $3, "last_sign_in_ip" = $4, "sign_in_count" = $5, "updated_at" = $6 WHERE "users"."id" = $7 [["current_sign_in_at", "2018-06-28 01:07:42.733295"], ["last_sign_in_at", "2018-06-28 01:07:42.733295"], ["current_sign_in_ip", "172.19.0.1/32"], ["last_sign_in_ip", "172.19.0.1/32"], ["sign_in_count", 1], ["updated_at", "2018-06-28 01:07:42.737637"], ["id", 1]]
これでは、 after_updateでメールを送っているためサインイン時にもメールが飛んでしまいます。
そこで、 条件付きコールバックを利用して、コールバックが実行されるべきか否かをチェックすることにします。
+ after_update :send_approved if: [:saved_changes?, :saved_change_to_approved?]
saved_changes?
メソッドはレコードに保存されていないオブジェクトがあるかどうかをチェックします。
このメソッドは、どのコールバックで実行するかによって、結果が変化します。
* 参照 -> https://qiita.com/ysKey2/items/7e429e478069b61b53bd
いずれにせよ、これだけではサインイン時にもメールが飛んでしまうことを解決できないため、 saved_change_to_approved?
で approvedに変更が加わっているか否かをチェック
します。
このメソッドを利用しているプロジェクトでは管理画面のみ登録申請を行なったユーザーの承認チェックを行なっているためひとまずこれで、approvedに変更が加わっている = 管理画面で承認されたということになります。
なお、Rails 5.1以前では save_changes?メソッドではなく changed?メソッドが使われていましたが、changed? メソッドは Deprecate
されているため、これから使うなら save_changes? を使いましょう。
まとめ
以上、短いですが 急遽deviseのバグ対応を行なっていた際に調べたことをまとめて執筆しました。
おそらく、これからもdevise関係で何かとカスタマイズするかと思いますので、その際はバグ対応中心に執筆していきたいと思います(deviseのカスタマイズ関係の記事は他の方が多く書かれていますしね)
それでは。