Created
June 21, 2011 22:14
-
-
Save namelessjon/1039058 to your computer and use it in GitHub Desktop.
Example user with a BCrypt password
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'bcrypt' | |
class User | |
include DataMapper::Resource | |
attr_accessor :password, :password_confirmation | |
timestamps :at | |
property :id, Serial | |
property :username, String, :length => 4..30, :unique => true, :required => true | |
property :crypted_pass, String, :length => 60..60, :required => true, :writer => :protected | |
property :email, String, :length => 5..200, :required => true, | |
:format => :email_address | |
validates_presence_of :password, :password_confirmation, :if => :password_required? | |
validates_confirmation_of :password, :if => :password_required? | |
before :valid?, :crypt_password | |
# check validity of password if we have a new resource, or there is a plaintext password provided | |
def password_required? | |
new? or password | |
end | |
def reset_password(password, confirmation) | |
update(:password => password, :password_confirmation => confirmation) | |
end | |
# Hash the password using BCrypt | |
# | |
# BCrypt is a lot more secure than a hash made for speed such as the SHA algorithm. BCrypt also | |
# takes care of adding a salt before hashing. The whole thing is encoded in a string 60 bytes long. | |
def crypt_password | |
self.crypted_pass = BCrypt::Password.create(password) if password | |
end | |
# Prepare a BCrypt hash from the stored password, overriding the default reader | |
# | |
# return the `:no_password` symbol if the property has no content. This is for | |
# the safety of the authenticate method. It's easy to pass a nil password to | |
# that method, but passing a specific symbol takes effort. | |
def crypted_pass | |
pass = super | |
if pass | |
BCrypt::Password.new(pass) | |
else | |
:no_password | |
end | |
end | |
def authenticate(password) | |
crypted_pass == password | |
end | |
def self.authenticate(username, password) | |
un = username.to_s.downcase | |
u = first(:conditions => ['lower(email) = ? OR lower(username) = ?', un, un]) | |
if u && u.authenticate(password) | |
u | |
else | |
nil | |
end | |
end | |
end |
@namelessjon I think you're right on all counts :)
If you want to write up something for the DM site that would be awesome. I know I've had to write this kind code lots of times before, and I bet it comes up fairly often.
for some reason the reset password method didn't work like that for me, so here is what I did :
def reset_password(password, confirmation)
if (password == confirmation)
self.password_reset = true
self.password = password
self.password_confirmation = confirmation
self.crypt_password
end
end
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I had some reason for not using the BCryptHash type at the time this code was originally written. I think it might have been as simple as at the time the model was added, I was only using that type and didn't want the extra gem dependency for what is very few lines of code.
Yes, it could.
I've never used devise. It's always seemed like a massive sledgehammer compared to the nut of a few methods needed to implement a simple auth scheme. Which is all I've ever needed.
I think an example is better than a module. Things like the
self.authenticate
method are almost certain to be written to taste. I could look into writing up a simple example for the DataMapper blog knowtheory has lying around or for the DataMapper site.