- 更新日: 2013年12月14日
- Devise
Devise の reset_password_token カラムを見て、MySQL のユニークキーが NULL 重複を許可するのを思い出した
Rails4 にて Devise でユーザー登録・ログイン認証・認可の機能を追加 で最初の Devise 用のマイグレーションで、create_table を行いました。
さらに、Rails4 で Devise と OmniAuth で、Twitter/Facebook のOAuth認証と通常フォームでの認証を併用して実装 で、OmniAuth 用のカラムを追加するマイグレーションを行い、OAuth 認証機能を追加しました。
その後、MySQL の中身を眺めたりしてたら思い出したのが表題の件。
MySQL DBのテーブル構造とそのマイグレーションを確認
データベースのテーブル構造は以下の様な状態。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
mysql> desc users; +------------------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------------------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | email | varchar(255) | NO | UNI | | | | encrypted_password | varchar(255) | NO | | | | | reset_password_token | varchar(255) | YES | UNI | NULL | | | reset_password_sent_at | datetime | YES | | NULL | | | remember_created_at | datetime | YES | | NULL | | | sign_in_count | int(11) | NO | | 0 | | | current_sign_in_at | datetime | YES | | NULL | | | last_sign_in_at | datetime | YES | | NULL | | | current_sign_in_ip | varchar(255) | YES | | NULL | | | last_sign_in_ip | varchar(255) | YES | | NULL | | | created_at | datetime | YES | | NULL | | | updated_at | datetime | YES | | NULL | | | uid | varchar(255) | NO | MUL | | | | provider | varchar(255) | NO | | | | | name | varchar(255) | YES | | NULL | | +------------------------+--------------+------+-----+---------+----------------+ 16 rows in set (0.00 sec) |
reset_password_token は Null を許可し(Yes)、かつ Key がユニークキー(UNI)と指定されています。
最初の create_table のマイグレーションは以下の通りだったので妥当です。(デフォルトのままでコメントアウト部分は削除。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
class DeviseCreateUsers < ActiveRecord::Migration def change create_table(:users) do |t| ## Database authenticatable t.string :email, :null => false, :default => "" t.string :encrypted_password, :null => false, :default => "" ## Recoverable t.string :reset_password_token t.datetime :reset_password_sent_at ## Rememberable t.datetime :remember_created_at ## Trackable t.integer :sign_in_count, :default => 0, :null => false t.datetime :current_sign_in_at t.datetime :last_sign_in_at t.string :current_sign_in_ip t.string :last_sign_in_ip t.timestamps end add_index :users, :email, :unique => true add_index :users, :reset_password_token, :unique => true end end |
reset_password_token には :null => false のオプションが付いていない。そして add_index で :unique => true のオプションが付いている。
User のレコードを何件か追加して、reset_password_token を確認したら NULL が重複してる!
ローカル環境で、試しにTwitter/FacebookでのOAuth認証や、通常のフォームからユーザー登録を行ってみると、テーブルのレコードは以下のようになりました。reset_password_token は全部 NULL になってますね、ユニークなはずなのにあれ?!
1 2 3 4 5 6 7 8 9 10 11 |
mysql> select * from users; +----+--------------------------------------------------+--------------------------------------------------------------+----------------------+------------------------+---------------------+---------------+---------------------+---------------------+--------------------+-----------------+---------------------+---------------------+--------------------------------------+----------+-----------------+ | id | email | encrypted_password | reset_password_token | reset_password_sent_at | remember_created_at | sign_in_count | current_sign_in_at | last_sign_in_at | current_sign_in_ip | last_sign_in_ip | created_at | updated_at | uid | provider | name | +----+--------------------------------------------------+--------------------------------------------------------------+----------------------+------------------------+---------------------+---------------+---------------------+---------------------+--------------------+-----------------+---------------------+---------------------+--------------------------------------+----------+-----------------+ | 1 | ******@gmail.com | $2a$10$cZW.VPZabBP/x4pG7xdCBOpvXyUDuKGIl5pPnKoQ6bbY21HOZZYaC | NULL | NULL | NULL | 1 | 2013-12-13 02:42:41 | 2013-12-13 02:42:41 | 127.0.0.1 | 127.0.0.1 | 2013-12-13 02:42:41 | 2013-12-13 02:42:41 | ********** | facebook | Takafumi Yamano | | 2 | 9036c5a5-8c9b-464e-bd1f-0a7e87a9cb23@example.com | $2a$10$LhDRdDk3ZcH2ODwc1FqqruvAmUS4xvQqFC03U/z1Dltu2UIfrnG/i | NULL | NULL | NULL | 1 | 2013-12-13 02:43:25 | 2013-12-13 02:43:25 | 127.0.0.1 | 127.0.0.1 | 2013-12-13 02:43:25 | 2013-12-13 02:43:25 | ******** | twitter | taka222 | | 3 | taka@example.com | $2a$10$BcQZFWnvQzpt3qAmK1p4iuPJX29YmgXb8hWB3X72lxOSa6Fx8wyRm | NULL | NULL | NULL | 1 | 2013-12-13 02:45:50 | 2013-12-13 02:45:50 | 127.0.0.1 | 127.0.0.1 | 2013-12-13 02:45:50 | 2013-12-13 02:45:50 | 25cdc772-fd28-4000-86d1-0f3700a45a7b | | NULL | +----+--------------------------------------------------+--------------------------------------------------------------+----------------------+------------------------+---------------------+---------------+---------------------+---------------------+--------------------+-----------------+---------------------+---------------------+--------------------------------------+----------+-----------------+ 3 rows in set (0.00 sec) |
reset_password_token というカラム名から推測すると、パスワードリセットの際に使われるトークンなのでしょう。パスワードリセットのリクエストがあったら、該当ユーザーにEメールを送信し、パスワードリセット用のURLをクリックしてもらって、レコードに保存されたトークンと照らし合わせて、トークンが同じだったらパスワード再設定画面を表示してパスワードを変更できる…的な。試してないので予想ですけど。
最初これに気付いた時、なぜ reset_password_token カラムはユニークキー制約してるのに、NULL が重複できるの!?ってなったのですが、その後 MySQL がそういう仕様だったのを思い出しました。そして、これトークンで NULL を指定すれば、他のユーザーのパスワードをリセットできるんじゃないのか?とか不安になったのですが、NULL のトークンを受け付けないなど、きっとしっかり作ってあるはずですよね。このあたりの Devise の動作も、今度確認しないといけない。
- Devise の関連記事
- RailsのDevise認証機能での実装チェックリストまとめ
- Deviseで送信されるメールのfrom(送信者メールアドレス)を変更
- Facebook の OAuth 認証で OAuthException(191)エラー
- Rails Devise でパスワードリセットなどのメールテンプレート(Mailer ビュー)をカスタマイズ
- Rails + Devise 環境でのフレンドリーフォワーディング機能を修正
- Deviseでユーザー登録完了時にウェルカムメールを送信する
- Rails Devise でユーザーがプロフィール情報を更新後に元のページにリダイレクトさせる
- Devise でユーザーがパスワードなしでアカウント情報を変更するのを許可
- Rails Deviseの日本語化辞書ファイル(devise.ja.yml)
- Rails + Devise で admin ユーザー(管理者)を削除できないようにする
Leave Your Message!