Overview
This gem provides a quick and dirty way to add secure hashing to your
ActiveRecord models. It relies on the recent refactoring of
ActiveRecord::Base::serialize
thus Rails 3.1+ is required.
Motivation
I wrote this gem in response to Aaron Patterson's request for a
has_secure_password
implementation that uses the newfound flexibility of
ActiveRecord::Base::serialize
. I originally intended to refactor the
has_secure_password
method in Rails 3.1, but that poses a problem that
stems from the fact that has_secure_password
is defined in ActiveModel::SecurePassword,
while all of the column serialization jazz is part of ActiveRecord::Base
.
Either SecurePassword
would need to be moved to ActiveRecord
, or all of
the serialize
jazz would need to be moved to ActiveModel
. The first
approach is the easiest, and at the moment only ActiveRecord::Base
mixes-in
the ActiveModel::SecurePassword
module, but it still takes away the option
of using it in, say, ActiveResource::Base
. The second approach would be
a pretty involved undertaking, and while the work done by serialize
and the
coders does not depend upon ActiveRecord
, no actual serialization is
performed until the model is persisted. As a result, it probably doesn't make
much sense to refactor serialize
into ActiveModel
.
In either case, such a refactoring would require some amount of pull amongst
the Rails community (both components mentioned were put in place by some
important Rails people) and unlike Jackie Treehorn, I don't pull shit in this
town. So, I created this gem to illustrate how dead simple the new hotness
of serialize
makes this feature. It should work just fine with
ActiveRecord
, but it was built as more of a proof of concept than anything
else.
Usage
Here's an example of how you might use this gem:
# Schema: users(:password_hash => String, :bollocks => String,
# :fancy_pants => String)
class User < ActiveRecord::Base
securely_hashes :password, :to => :password_hash
securely_hashes :bollocks
class AlternateCoder
def self.dump some_value
# ...
end
def self.load encoded
# ...
end
end
securely_hashes :fancy_pants, :with => AlternateCoder
end
some_user = User.new
some_user.password = 'super secret'
some_user.bollocks = 'something else to hash'
some_user.fancy_pants = 'you get the idea'
some_user.save
some_user.reload
some_user.password_hash # => $2a$10blahetcetc...
some_user.bollocks # => $2a$10yaddayadda...
When using the :to => <column name>
option, the gem will create getters and
setters for the given attribute name and the setter will pass the value on
to the actual column. So, in our example of
securely_hashes :password, :to => :password_hash
the gem defined password
and password=
methods on our model automatically.
This behavior is important to note because it can clobber (or be clobbered) by
methods explicitly defined on the model. If the :to => ...
option is not
used, no methods are created.
Installing
You can use this gem in your rails app by adding
gem 'securely_hashed_attributes'
to your Gemfile
. Alternatively, you can install the gem directly:
gem install securely_hashed_attributes
or you can clone the git repo:
git clone git://github.com/iande/securely_hashed_attributes.git
I want to re-iterate that this gem was written mostly as a proof of concept, and you must be running Rails 3.1+ to use it.
Further Thoughts
Hopefully this gem serves its purpose of demonstrating how easy it is to do
some pretty cool shit with serialized columns in Rails 3.1. You could easily
add a coder that handles encryption to securely persist data in a column and
semi-automagically work with the unencrypted data in your app. It also
provides opportunities for leveraging features of your database of choice, as
Aaron Patterson demonstrated with his HStore coder at RailsConf 2011.
Although, you may not want to use eval
to decode the data.
License
Public Domained!
Contributing
I'm pretty open to pull requests, if anyone finds utility in this gem and wants to contribute additional features. All I ask is that you provide adequate documentation and test coverage for any code you contribute. If you find the "public domaining" of this code to be problematic, fork the code and put it under whatever license you like. You're free to do with this code pretty much whatever you like.