Because rails clears the errors during save, I can't do this in my model:
def password= plain_text
if plain_text =~ /\d/ && plain_text =~ /\w/
self.password_digest = BCrypt::Password.create(plain_text)
else
self.errors[:password] = "must contain one digit and one word character."
end
end
What's the best way to validate a password to have one letter and one digit in rails, while still using bcrypt?
If you are using the Rails has_secure_password
option, then Rails will handle the password=
method for you. When you set the password Rails will hold onto the unencrypted password in the instance variable @password
:
def password=(unencrypted_password)
...
@password = unencrypted_password
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
self.password_digest = BCrypt::Password.create(unencrypted_password, cost: cost)
The @password
value will remain in memory until the record is saved to the database. So you can run validations against password
. When validations pass, the encrypted password_digest
value is what will be stored.
You can see that this is what Rails does when they validate that the password isn't longer than the largest password allowed:
validates_length_of :password, maximum: ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED