Skip to content

Commit

Permalink
specin' it out
Browse files Browse the repository at this point in the history
  • Loading branch information
Ian D. Eccles committed May 19, 2011
1 parent 67fad0f commit b3a476e
Show file tree
Hide file tree
Showing 9 changed files with 176 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-f progress
-c
1 change: 1 addition & 0 deletions init.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require 'securely_hashed_attributes'
31 changes: 30 additions & 1 deletion lib/securely_hashed_attributes.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,32 @@
# Kind of necessary for, you know, everything else we do.
require 'bcrypt'
require 'active_record'

module SecurelyHashedAttributes
# Your code goes here...
DEFAULT_ATTRIBUTE_OPTIONS = {
:to => nil,
:with => nil
}
end
require 'securely_hashed_attributes/version'
require 'securely_hashed_attributes/coders'

# Boom, boom, add a method!
class << ActiveRecord::Base
def securely_hashes attrib, opts={}
opts = SecurelyHashedAttributes::DEFAULT_ATTRIBUTE_OPTIONS.merge opts
attrib = attrib.to_sym
if opts[:to]
col = opts[:to].to_sym
serialize col, opts[:with]

attr_reader attrib
define_method :"#{attrib}=" do |val|
instance_variable_set(:"@#{attrib}", val)
self[col] = val
end
else
serialize attrib, opts[:with]
end
end
end
4 changes: 4 additions & 0 deletions lib/securely_hashed_attributes/coders.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module SecurelyHashedAttributes::Coders
end

require 'securely_hashed_attributes/coders/bcrypt_password_column'
18 changes: 18 additions & 0 deletions lib/securely_hashed_attributes/coders/bcrypt_password_column.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class SecurelyHashedAttributes::Coders::BCryptPasswordColumn
RESCUE_ERRORS = [ArgumentError, BCrypt::Errors::InvalidHash]

def self.dump(obj)
BCrypt::Password.create(obj)
end

def self.load(bcrypt)
return '' if bcrypt.nil?
begin
BCrypt::Password.new(bcrypt)
rescue *RESCUE_ERRORS
bcrypt
end
end

SecurelyHashedAttributes::DEFAULT_ATTRIBUTE_OPTIONS[:with] = self
end
4 changes: 4 additions & 0 deletions securely_hashed_attributes.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,8 @@ Gem::Specification.new do |s|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]
s.add_development_dependency 'rails', '~> 3.1.0.beta1'
s.add_development_dependency 'rspec'
s.add_development_dependency 'with_model'
s.add_development_dependency 'sqlite3'
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
require 'spec_helper'

describe SecurelyHashedAttributes::Coders::BCryptPasswordColumn do
it "should dump a string as a bcrypt password hash" do
hashed = SecurelyHashedAttributes::Coders::BCryptPasswordColumn.dump('my stringzy')
hashed.should be_a_kind_of(BCrypt::Password)
end

it "should dump a non string to bcrypt by converting it to a string" do
hashed = SecurelyHashedAttributes::Coders::BCryptPasswordColumn.dump([1, 2, 3])
hashed.should be_a_kind_of(BCrypt::Password)
end

it "should load a bcrypt hash as a string" do
hashed = SecurelyHashedAttributes::Coders::BCryptPasswordColumn.dump('my stringzy')
loaded = SecurelyHashedAttributes::Coders::BCryptPasswordColumn.load(hashed)
loaded.should be_a_kind_of(BCrypt::Password)
loaded.should == 'my stringzy'
end

it "should load a nil as an empty string" do
loaded = SecurelyHashedAttributes::Coders::BCryptPasswordColumn.load(nil)
loaded.should_not be_a_kind_of(BCrypt::Password)
loaded.should == ''
end

it "should load a non bcrypt hash as a plain string" do
loaded = SecurelyHashedAttributes::Coders::BCryptPasswordColumn.load('sweet lameness')
loaded.should_not be_a_kind_of(BCrypt::Password)
loaded.should == 'sweet lameness'
end
end
69 changes: 69 additions & 0 deletions spec/securely_hashed_attributes_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
require 'spec_helper'

require 'active_record/connection_adapters/sqlite3_adapter'

ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")

describe ActiveRecord::Base do
class AlternateCoder
def dump(*_); 'encoded'; end
def load(*_); 'decoded'; end
end

let(:new_model) {
HashingModel.new
}

with_model :hashing_model do
table do |t|
t.string :blathering
t.string :password_hash
t.string :chicken_fist_digest
end

model do
attr_reader :stately_dutch
alias :stately_dutch? :stately_dutch

securely_hashes :blathering, :with => AlternateCoder
securely_hashes :password, :to => :password_hash
securely_hashes :chicken_fist, :to => :chicken_fist_digest

def chicken_fist=(bird)
@stately_dutch = true
end
end
end

before(:each) do
HashingModel.delete_all
end

it "should create getters and setters for :password" do
new_model.should respond_to(:password)
new_model.should respond_to(:password=)
new_model.password = 'super duper'
new_model.password.should == 'super duper'
end

it "should serialize :password to :password_digest on save" do
new_model.password = 'super duper'
new_model.save
old_model = HashingModel.find(new_model.id)
old_model.password_hash.should_not be_empty
old_model.password_hash.should be_a_kind_of(BCrypt::Password)
old_model.password_hash.should == 'super duper'
end

it "should get clobbered by #chicken_fist=" do
new_model.should_not be_stately_dutch
new_model.chicken_fist = 'a string to hash'
new_model.should be_stately_dutch
end

it "should not hash properly because of #chicken_fist=" do
new_model.chicken_fist = 'a string to hash'
new_model.save
new_model.chicken_fist_digest.should be_empty
end
end
16 changes: 16 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Dir[File.expand_path('support', File.dirname(__FILE__)) + "/**/*.rb"].each { |f| require f }

begin
require 'simplecov'
SimpleCov.start do
add_filter "/spec/"
end
rescue LoadError
end

require 'securely_hashed_attributes'
require 'with_model'

RSpec.configure do |config|
config.extend WithModel
end

0 comments on commit b3a476e

Please sign in to comment.