Matthew Todd

HasDigest

Finding I was generating SHA1 digests in a bunch of places in my code, I spent Friday writing up a quick rails plugin (with tests and a helpful shoulda macro) to make things easier:

class User < ActiveRecord::Base
  has_digest :encrypted_password, :depends => :password
  has_digest :remember_me_token, :depends => [:login, :remember_me_token_expires_at]
  has_digest :token
end

For comparison’s sake, here’s how the User model used to look:

class User < ActiveRecord::Base
  attr_accessor :password

  before_save :write_salt
  before_save :write_encrypted_password
  before_save :write_token

  def remember_me
    self.remember_me_token_expires_at = 2.weeks.from_now
    self.remember_me_token            = encrypt("--#{login}--#{remember_me_token_expires_at}--")
  end

  private

  def write_salt
    self.salt = Digest::SHA1.hexdigest(random_string) if new_record?
  end

  def write_encrypted_password
    self.encrypted_password = encrypt(password) unless password.blank?
  end

  def write_token
    if token.blank? || requesting_new_token
      self.requesting_new_token = false
      self.token = encrypt(random_string)
    end
  end

  def encrypt(password)
    Digest::SHA1.hexdigest("--#{salt}--#{password}--")
  end

  def random_string
    Time.now.to_s.split(//).sort_by { rand }.join
  end
end

I suppose this kind of thing is primarily useful if you’re (as I was, for the sake of learning) putting together your own authentication system, though it also comes in handy when you need a non-guessable token for, say, a reset password request or a store order number.

I would also be remiss not to point out that my test_helper owes its existence entirely to its counterpart in the (wonderful!) Paperclip.

Have fun!

Comments