Creating simple unique tokens in Ruby with Hasher
2009/10/12 // Leeds // // Feed
Randomly hashed tokens have thousands of uses. One of them is creating a random secure hash as a handle on a user account:
http://awesomewebapp.com/confirm/843e9d74d716b41517addde515f4e6db940be21d
A random unique hash is created and assigned to your account, allowing you to confirm your identity through a link without disclosing any personal information about who you are. Hashes like this can easily be created by digesting a single string or a string with an additional salt injected into it to ensure the original string is even more indecipherable.
I was seeing quite a lot of duplication in areas of my code where I was constructing hashes based on certain attributes of a particular class. This required me to build up a hash-able string and then pass it through SHA1 to digest it. Although this is only a few lines of code, its nice to bundle this up into a reusable module so you can just do something like this:
generate_hash "somevalueiwanttohash" # => 1a09a8b3dd4b24d5284ac13705523d1b15b55e64
Thats where Hasher comes in, a simple module you can mix into any Ruby project. Heres the code:
# hasher.rb
# git://github.com/joshnesbitt/hasher.git
module Hasher
require 'digest/sha1'
SECRET = "ilikebigbuttsandicannotlie"
def generate_hash(string)
encrypt string
end
protected
def encrypt(string)
Digest::SHA1.hexdigest(salt + string)
end
def salt
Digest::SHA1.hexdigest(SECRET + time_stamp + random)
end
def time_stamp(time = Time.now)
time.strftime("%d%Y%l%M").to_s
end
def random(size = 10)
(0...size).map { character_set[ rand(character_set.size) ] }.join
end
def character_set
[('0'..'9'),('A'..'Z')].map { |range| range.to_a }.flatten
end
end
So say you want to create a confirmation token for a user before_create, you would use it in an ActiveRecord model like this:
# app/models/user.rb
class User < ActiveRecord::Base
include Hasher
# ... other ruby code ...
before_create :generate_confirmation_token
protected
def generate_confirmation_token
self.confirmation_token = generate_hash("#{self.id}-#{self.email}")
end
end
Its not much, but if you end up having more models needing similar functionality it makes sense to package it up into a nice mixin.