Permalink
Browse files

Added blowfish password encryption strategy.

  • Loading branch information...
1 parent 3430e88 commit 40362e92c81f2e5b9414f98ce85ff8f03fa83bd2 squarism committed Feb 26, 2012
Showing with 100 additions and 1 deletion.
  1. +9 −1 README.md
  2. +1 −0 lib/clearance/password_strategies.rb
  3. +45 −0 lib/clearance/password_strategies/blowfish.rb
  4. +45 −0 spec/models/blowfish_spec.rb
View
@@ -195,14 +195,22 @@ By default, Clearance uses SHA1 encryption of the user's password. You can provi
def encrypt_password
end
-See [lib/clearance/password_strategies/sha1.rb](https://github.com/thoughtbot/clearance/blob/master/lib/clearance/password_strategies/sha1.rb) for the default behavior.
+See [lib/clearance/password_strategies/sha1.rb](https://github.com/thoughtbot/clearance/blob/master/lib/clearance/password_strategies/sha1.rb) for the default behavior. Also see [lib/clearance/password_strategies/blowfish.rb](https://github.com/thoughtbot/clearance/blob/master/lib/clearance/password_strategies/blowfish.rb) for another password strategy. Switching password strategies will cause your existing users' passwords to not work.
Once you have an API-compliant module, load it with:
Clearance.configure do |config|
config.password_strategy = MyPasswordStrategy
end
+For example:
+
+ # default
+ config.password_strategy = Clearance::PasswordStrategies::SHA1
+ # ... or another example
+ config.password_strategy = Clearance::PasswordStrategies::Blowfish
+
+
Optional Cucumber features
--------------------------
@@ -1,5 +1,6 @@
module Clearance
module PasswordStrategies
autoload :SHA1, 'clearance/password_strategies/sha1'
+ autoload :Blowfish, 'clearance/password_strategies/blowfish'
end
end
@@ -0,0 +1,45 @@
+require 'openssl'
+
+module Clearance
+ module PasswordStrategies
+ module Blowfish
+ extend ActiveSupport::Concern
+
+ # Am I authenticated with given password?
+ #
+ # @param [String] plain-text password
+ # @return [true, false]
+ # @example
+ # user.authenticated?('password')
+ def authenticated?(password)
+ encrypted_password == encrypt(password)
+ end
+
+ protected
+
+ def encrypt_password
+ initialize_salt_if_necessary
+ if password.present?
+ self.encrypted_password = encrypt(password)
+ end
+ end
+
+ def generate_hash(string)
+ # TODO: 1.9 vs 1.8 testing
+ cipher = OpenSSL::Cipher::Cipher.new('bf-cbc').encrypt
+ cipher.key = Digest::SHA256.digest(salt)
+ cipher.update(string) << cipher.final
+ end
+
+ def encrypt(string)
+ generate_hash("--#{salt}--#{string}--")
+ end
+
+ def initialize_salt_if_necessary
+ if salt.blank?
+ self.salt = generate_random_code
+ end
+ end
+ end
+ end
+end
@@ -0,0 +1,45 @@
+require 'spec_helper'
+
+describe Clearance::PasswordStrategies::Blowfish do
+ subject do
+ Class.new do
+ attr_accessor :salt, :password, :encrypted_password
+ include Clearance::PasswordStrategies::Blowfish
+
+ def generate_random_code; "code"; end
+ end.new
+ end
+
+ describe "#encrypt_password" do
+ context "when the password is set" do
+ let(:salt) { "salt" }
+ let(:password) { "password" }
+
+ before do
+ subject.salt = salt
+ subject.password = password
+ subject.send(:encrypt_password)
+ end
+
+ it "should encrypt the password using Blowfish into encrypted_password" do
+ cipher = OpenSSL::Cipher::Cipher.new('bf-cbc').encrypt
+ cipher.key = Digest::SHA256.digest(salt)
+ expected = cipher.update("--#{salt}--#{password}--") << cipher.final
+
+ subject.encrypted_password.should == expected
+ end
+ end
+
+ context "when the salt is not set" do
+ before do
+ subject.salt = nil
+
+ subject.send(:encrypt_password)
+ end
+
+ it "should initialize the salt" do
+ subject.salt.should_not be_nil
+ end
+ end
+ end
+end

0 comments on commit 40362e9

Please sign in to comment.