#ruby #rspec #sequel #roda
#рубиновый #rspec #продолжение #рода
Вопрос:
У меня есть API, построенный с помощью стека Roda Sequel. Вот как 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 #=> "$2a$12$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 #=> "$2a$12$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
Может ли кто-нибудь объяснить мне, почему мой первый пример терпит неудачу?
Комментарии:
1.
is_password?
является псевдонимом для==
so, оба должны пройти. Что делатьuser.password
иBCrypt::Password.new(user.password_hash)
возвращаться? (из ваших примеров RSpec)2. Есть такой плагин для продолжения github.com/mlen/sequel_secure_password . Он давно не обновлялся, но, похоже, работает.
Ответ №1:
Перепишите это
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
=> "$2a$12$FktSw7HPYEUYSPBdmmsiXe26II6UV5gvyn2ECwOflTYHP94Hrm2mS"
'$2a$12$FktSw7HPYEUYSPBdmmsiXe26II6UV5gvyn2ECwOflTYHP94Hrm2mS' == 'new-password'
=> false
и чтобы сравнить их, вам нужно зашифровать свой «новый пароль» и сравнить два хэша. Вы можете легко зашифровать любую строку с помощью bcrypt, но для ее расшифровки потребуется много времени (годы). Обычно перед проверкой пароля зашифруйте свой input password
пароль тем же алгоритмом, который содержит ваша база данных
Ответ №2:
Вы сами отвечаете на свой вопрос: вы хэшируете пароль перед его сохранением (в установщике паролей в User.rb
:
# It sets password_hash column with hashed user password.
#
# @return [String] user password hash.
#
# @example Set {User} password:
# User.new(password: 'test').password_hash #=> "$2a$12$FktSw7HPYEUYSPBdmmsiXe26II6UV5gvyn2ECwOflTYHP94Hrm2mS"
def password=(new_password)
@password = BCrypt::Password.create(new_password)
self.password_hash = @password
end
В вашем первом тесте вы пытаетесь сравнить хэшированный пароль с неэшированной строкой.