I'm experiencing a very weird problem with Ohm that I'm not able to track and solve. Ohm version is 2.0.1.
This is my code:
class User < Ohm::Model
attribute :username
attribute :password
index :username
def password= string
@attributes[:password] = BCrypt::Password.create(string) # Tried self.password = BCrypt::whatever too
end
end
[15] pry(main)> User.find(username: 'test').first.password
=> "$2a$10$j1.s4hmuyCm8RffaEvB8IejaYOiZXWXId1Ccf8S0K3uXduxmMzyUq"
[16] pry(main)> User.find(username: 'test').first.password
=> "$2a$10$/0UzWtVsF.xczf4.UUqrP.PqYHxKs8fkIWKHlVVQVUNPFubzmuCwO"
[17] pry(main)> User.find(username: 'test').first.password
=> "$2a$10$ajlc3BYMOFXYDmy1a112ieXhMm39KoR1wPdPMp4WwEnxb2E35ypvC"
[18] pry(main)> User.find(username: 'test').first.password
=> "$2a$10$TlW87Gpd4RKpPutWzkePqeQiGri2ah.txDda4o6Lki7Sk1vayY9Fm"
Basically I'm able to set the password and encrypt it using BCrypt, but for some reasons every time I call the attribute the password is different. I have no idea what's happening here, can someone help me?
When Ohm loads the attributes from the database, it doesn't assign it to @attributes directly. Instead, it uses the accessor to allow for any kinds of type casting. In your case, what's happening is that each time Ohm loads the password field from Redis, it processes it again with BCrypt. You can check the code in question.
What I usually do is a bit different: I define an attribute
crypted_password
in Ohm, and then in the model I define a method
password=
that takes a string and produces the crypted version,
which is then stored in the crypted_password
attribute. I use a gem
called Shield, here's the relevant code.
You can do something similar to what Shield does, and use BCrypt
instead. You have to define an attribute crypted_password
, then a
method called password=
that is a bit different from what you
currently have:
class User < Ohm::Model
attribute :username
attribute :crypted_password
index :username
def password= string
self.crypted_password = BCrypt::Password.create(string)
end
end
That should work, and you will have to use crypted_password
for
authenticating users.