使用 RSpec 测试用户密码更新
Testing user password update with RSpec
我有一个使用 Roda + Sequel 堆栈构建的 API。这是我的 User
模型的样子:
# frozen_string_literal: true
# {User} is model responsible for storing {User} authentication informations like email and password.
#
# @!attribute id
# @return [UUID] ID of the {User} in UUID format.
#
# @!attribute email
# @return [String] Email of the {User}, it's stored in the PostgreSQL citext column.
#
# @!attribute password_hash
# @return [String] {User} hashed password with bcrypt.
#
# @!attribute created_at
# @return [DateTime] Time when {User} was created.
#
# @!attribute updated_at
# @return [DateTime] Time when {User} was updated
class User < Sequel::Model
# It returns instance BCrypt::Password based on value in password_hash column.
#
# @return [BCrypt::Password ] based on value in password_hash column.
#
# @example Get {User} password hash:
# User.new(password: 'test').password #=> "a$FktSw7HPYEUYSPBdmmsiXe26II6UV5gvyn2ECwOflTYHP94Hrm2mS"
def password
@password ||= BCrypt::Password.new(password_hash)
end
# It sets password_hash column with hashed user password.
#
# @return [String] user password hash.
#
# @example Set {User} password:
# User.new(password: 'test').password_hash #=> "a$FktSw7HPYEUYSPBdmmsiXe26II6UV5gvyn2ECwOflTYHP94Hrm2mS"
def password=(new_password)
@password = BCrypt::Password.create(new_password)
self.password_hash = @password
end
end
我有以下测试:
describe 'update user password' do
let(:params) { { password: 'new-password' } }
before do
put '/api/v1/update_password', params
user.reload
end
it 'returns 200 HTTP status' do
expect(response.status).to eq 200
end
This one is failing.
it 'updates user password' do
expect(user.password == 'new-password').to eq true
end
# This one is passing.
it 'updates user password' do
expect(BCrypt::Password.new(user.password_hash).is_password?('new-password')).to eq true
end
end
这个例子失败了:
it 'updates user password' do
expect(user.password == 'new-password').to eq true
end
但是这个通过了:
it 'updates user password' do
expect(BCrypt::Password.new(user.password_hash).is_password?('new-password')).to eq true
end
有人可以向我解释为什么我的第一个示例失败了吗?
您是在自己回答问题:您在密码保存之前对密码进行哈希处理(在 User.rb
中的密码 setter:
# It sets password_hash column with hashed user password.
#
# @return [String] user password hash.
#
# @example Set {User} password:
# User.new(password: 'test').password_hash #=> "a$FktSw7HPYEUYSPBdmmsiXe26II6UV5gvyn2ECwOflTYHP94Hrm2mS"
def password=(new_password)
@password = BCrypt::Password.create(new_password)
self.password_hash = @password
end
在您的第一个测试中,您尝试将经过哈希处理的密码与未经哈希处理的字符串进行比较。
重写这个
it 'updates user password' do
expect(user.password == 'new-password').to eq true
end
到这个
it 'updates user password' do
expect(user.password).to eq(BCrypt::Password.create('new-password'))
end
您的 ruby 对象 User
包含该密码的散列版本
user.password
=> "a$FktSw7HPYEUYSPBdmmsiXe26II6UV5gvyn2ECwOflTYHP94Hrm2mS"
'a$FktSw7HPYEUYSPBdmmsiXe26II6UV5gvyn2ECwOflTYHP94Hrm2mS' == 'new-password'
=> false
为了比较它们,您必须对 'new-password' 进行加密并比较两个哈希值。您可以使用 bcrypt 轻松加密任何字符串,但解密它会花费很多时间(数年)。通常在验证密码之前,使用数据库包含的相同算法加密您的 input password
我有一个使用 Roda + Sequel 堆栈构建的 API。这是我的 User
模型的样子:
# frozen_string_literal: true
# {User} is model responsible for storing {User} authentication informations like email and password.
#
# @!attribute id
# @return [UUID] ID of the {User} in UUID format.
#
# @!attribute email
# @return [String] Email of the {User}, it's stored in the PostgreSQL citext column.
#
# @!attribute password_hash
# @return [String] {User} hashed password with bcrypt.
#
# @!attribute created_at
# @return [DateTime] Time when {User} was created.
#
# @!attribute updated_at
# @return [DateTime] Time when {User} was updated
class User < Sequel::Model
# It returns instance BCrypt::Password based on value in password_hash column.
#
# @return [BCrypt::Password ] based on value in password_hash column.
#
# @example Get {User} password hash:
# User.new(password: 'test').password #=> "a$FktSw7HPYEUYSPBdmmsiXe26II6UV5gvyn2ECwOflTYHP94Hrm2mS"
def password
@password ||= BCrypt::Password.new(password_hash)
end
# It sets password_hash column with hashed user password.
#
# @return [String] user password hash.
#
# @example Set {User} password:
# User.new(password: 'test').password_hash #=> "a$FktSw7HPYEUYSPBdmmsiXe26II6UV5gvyn2ECwOflTYHP94Hrm2mS"
def password=(new_password)
@password = BCrypt::Password.create(new_password)
self.password_hash = @password
end
end
我有以下测试:
describe 'update user password' do
let(:params) { { password: 'new-password' } }
before do
put '/api/v1/update_password', params
user.reload
end
it 'returns 200 HTTP status' do
expect(response.status).to eq 200
end
This one is failing.
it 'updates user password' do
expect(user.password == 'new-password').to eq true
end
# This one is passing.
it 'updates user password' do
expect(BCrypt::Password.new(user.password_hash).is_password?('new-password')).to eq true
end
end
这个例子失败了:
it 'updates user password' do
expect(user.password == 'new-password').to eq true
end
但是这个通过了:
it 'updates user password' do
expect(BCrypt::Password.new(user.password_hash).is_password?('new-password')).to eq true
end
有人可以向我解释为什么我的第一个示例失败了吗?
您是在自己回答问题:您在密码保存之前对密码进行哈希处理(在 User.rb
中的密码 setter:
# It sets password_hash column with hashed user password.
#
# @return [String] user password hash.
#
# @example Set {User} password:
# User.new(password: 'test').password_hash #=> "a$FktSw7HPYEUYSPBdmmsiXe26II6UV5gvyn2ECwOflTYHP94Hrm2mS"
def password=(new_password)
@password = BCrypt::Password.create(new_password)
self.password_hash = @password
end
在您的第一个测试中,您尝试将经过哈希处理的密码与未经哈希处理的字符串进行比较。
重写这个
it 'updates user password' do
expect(user.password == 'new-password').to eq true
end
到这个
it 'updates user password' do
expect(user.password).to eq(BCrypt::Password.create('new-password'))
end
您的 ruby 对象 User
包含该密码的散列版本
user.password
=> "a$FktSw7HPYEUYSPBdmmsiXe26II6UV5gvyn2ECwOflTYHP94Hrm2mS"
'a$FktSw7HPYEUYSPBdmmsiXe26II6UV5gvyn2ECwOflTYHP94Hrm2mS' == 'new-password'
=> false
为了比较它们,您必须对 'new-password' 进行加密并比较两个哈希值。您可以使用 bcrypt 轻松加密任何字符串,但解密它会花费很多时间(数年)。通常在验证密码之前,使用数据库包含的相同算法加密您的 input password