はじめに
Rails 6 に追加されそうな新機能を試す第11段。 今回のちょい足し機能は、 has_secure_password
編です。
has_secure_password にオプションとしてカラムを指定できるようになりました。
記載時点では、Rails は 6.0.0.rc1 です。 gem install rails --prerelease
でインストールできます。
$ rails --version
Rails 6.0.0.rc1
プロジェクトを作成する
rails プロジェクトを作成します。
$ rails new sandbox6_0_0rc1
$ cd sandbox6_0_0rc1
Gemfile を編集して bcrypt gem を追加する
has_secure_password を使うために bcrypt gem を追加します。
Gemfile の bcrypt の行を有効にします。
# Use Active Model has_secure_password
gem 'bcrypt', '~> 3.1.7'
bundle
を実行します
$ bundle
User モデルを作成する
User モデルを作成します。
このとき、password の他に token 用の digest カラムを追加します。
$ bin/rails g model User name password_digest token_digest
$ bin/rails db:create db:migrate
User モデルを編集する
User model を編集し、 has_secure_password
を追加します。
:token
オプションを追加したものとオプションを省略したもの2行を追加します。
オプションを省略した場合は従来と同じですね。
class User < ApplicationRecord
has_secure_password # これは今までと同じ
has_secure_password :token, validations: true # こちらは新機能
end
has_secure_password
の機能を確認する
rails コンソールから確認してみます。
$ bin/rails c
Running via Spring preloader in process 393
Loading development environment (Rails 6.0.0.rc1)
オプションなしの方は今までと変わりません。 password
, password_confirmation
などのメソッドが使えます。
irb(main):001:0> u = User.new
=> #<User id: nil, name: nil, password_digest: nil, token_digest: nil, created_at: nil, updated_at: nil>
irb(main):002:0> u.password
=> nil
irb(main):003:0> u.password_confirmation
=> nil
オプションを指定した :token
に合わせて、 token
token_confirmation
などのメソッドが追加されます。
irb(main):004:0> u.token
=> nil
irb(main):005:0> u.token_confirmation
=> nil
validation を試してみると password
だけではなく token
についてもチェックしていることがわかります。
irb(main):006:0> u.valid?
=> false
irb(main):007:0> u.errors.full_messages
=> ["Password can't be blank", "Token can't be blank"]
token
と token_confirmation
が一致しているかどうかのチェックもしてくれます。
irb(main):008:0> u.password = 'password'
=> "password"
irb(main):009:0> u.password_confirmation = 'pass'
=> "pass"
irb(main):010:0> u.token = 'token'
=> "token"
irb(main):011:0> u.token_confirmation = 'tok'
=> "tok"
irb(main):012:0> u.valid?
=> false
irb(main):013:0> u.errors.full_messages
=> ["Password confirmation doesn't match Password", "Token confirmation doesn't match Token"]
password_confirmation
, token_confirmation
を設定してデータベースに保存してみましょう。
irb(main):014:0> u.password_confirmation = 'password'
=> "password"
irb(main):015:0> u.token_confirmation = 'token'
=> "token"
irb(main):016:0> u.name = 'user1'
=> "user1"
irb(main):017:0> u.save
(0.3ms) BEGIN
User Create (0.5ms) INSERT INTO "users" ("name", "password_digest", "token_digest", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["name", "user1"], ["password_digest", "$2a$10$uvvJuUHV8fNQ/0GRRtyewuboiAEUpD4Q1.0/coyWWhVxOivHnIMia"], ["token_digest", "$2a$10$56jLRgb1n0WjLOa/9NqIoOm3Il8nfyXCCisk5oieTxe27/EE56UpC"], ["created_at", "2019-05-03 23:04:03.971339"], ["updated_at", "2019-05-03 23:04:03.971339"]]
(7.3ms) COMMIT
=> true
今保存したデータを読み直します。
irb(main):022:0> u = User.last
User Load (0.6ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1 [["LIMIT", 1]]
=> #<User id: 2, name: "user1", password_digest: [FILTERED], token_digest: "$2a$10$56jLRgb1n0WjLOa/9NqIoOm3Il8nfyXCCisk5oieTxe...", created_at: "2019-05-03 23:04:03", updated_at: "2019-05-03 23:04:03">
password は今まで通り authenticate
で認証できます。
irb(main):023:0> u.authenticate('pass')
=> false
irb(main):024:0> u.authenticate('password')
=> #<User id: 2, name: "user1", password_digest: [FILTERED], token_digest: "$2a$10$56jLRgb1n0WjLOa/9NqIoOm3Il8nfyXCCisk5oieTxe...", created_at: "2019-05-03 23:04:03", updated_at: "2019-05-03 23:04:03">
token
は authenticate_token
で認証できます。
irb(main):025:0> u.authenticate_token('password')
=> false
irb(main):026:0> u.authenticate_token('token')
=> #<User id: 2, name: "user1", password_digest: [FILTERED], token_digest: "$2a$10$56jLRgb1n0WjLOa/9NqIoOm3Il8nfyXCCisk5oieTxe...", created_at: "2019-05-03 23:04:03", updated_at: "2019-05-03 23:04:03">
validations: false
にしたときは?
validations: false
のときは、validation のチェックをしません。 authenticate_token (authenticate_xxx) は使えます。
class User < ApplicationRecord
has_secure_password
has_secure_password :token, validations: false
end
irb(main):007:0> u = User.new
=> #<User id: nil, name: nil, password_digest: nil, token_digest: nil, created_at: nil, updated_at: nil>
irb(main):008:0> u.valid?
=> false
irb(main):009:0> u.errors.full_messages
=> ["Password can't be blank"]
irb(main):010:0> u.password = u.password_confirmation = 'password'
=> "password"
irb(main):011:0> u.token = 'token'
=> "token"
irb(main):012:0> u.save
(0.3ms) BEGIN
User Create (0.7ms) INSERT INTO "users" ("password_digest", "token_digest", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["password_digest", "$2a$10$NC1IH2S8G/RKX7myjNfeeepMknnQUuKIqD60S70iom3ZOQl92j/Cq"], ["token_digest", "$2a$10$xFYYXX7Bxk6qAOaqd3M7COlkZBCYodZK4HYQoBxLTNjr2dibMU/MK"], ["created_at", "2019-05-03 23:42:47.628292"], ["updated_at", "2019-05-03 23:42:47.628292"]]
(7.2ms) COMMIT
=> true
irb(main):013:0> u = User.last
User Load (0.6ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1 [["LIMIT", 1]]
=> #<User id: 3, name: nil, password_digest: [FILTERED], token_digest: "$2a$10$xFYYXX7Bxk6qAOaqd3M7COlkZBCYodZK4HYQoBxLTNj...", created_at: "2019-05-03 23:42:47", updated_at: "2019-05-03 23:42:47">
irb(main):014:0> u.authenticate_token('pass')
=> false
irb(main):015:0> u.authenticate_token('token')
=> #<User id: 3, name: nil, password_digest [FILTERED], token_digest: "$2a$10$xFYYXX7Bxk6qAOaqd3M7COlkZBCYodZK4HYrQoBxLTNj...", created_at: "2019-05-03 23:42:47", updated_at: "2019-05-03 23:42:47">"'"]
:validations
オプションを省略したときは?
validations: true
を指定したときと同じ動作になります。
おまけ
irb の出力で password_digest
が [FILTERED]
となっているのに token_digest
が [FILTERED]
にならないのは、
config/initializers/filter_parameter_logging.rb
で設定されていないからです。
token
も追加してあげれば [FILTERED]
になります。
# Configure sensitive parameters which will be filtered from the log file.
Rails.application.config.filter_parameters += [:password, :token]