Skip to content
This repository
Browse code

BCrypt for passwords

This commit makes BCrypt the default for new setups, and introduces a
strategy for converting existing infrastructure to BCrypt.

To switch to BCrypt now:

    Clearance.configure do |config|
      config.password_strategy = Clearance::PasswordStrategies::BCrypt
    end

To set the password strategy to the conversion layer:

    Clearance.configure do |config|
      config.password_strategy = Clearance::PasswordStrategies::BCryptMigrationFromSHA1
    end

To continue to use SHA1:

    Clearance.configure do |config|
      config.password_strategy = Clearance::PasswordStrategies::SHA1
    end
  • Loading branch information...
commit be37c354594a33333006dcdff38ffc46bdc55620 1 parent 3746806
authored October 23, 2011 mike-burns committed June 29, 2012
2  Gemfile.lock
@@ -2,6 +2,7 @@ PATH
2 2
   remote: .
3 3
   specs:
4 4
     clearance (0.16.2)
  5
+      bcrypt-ruby
5 6
       diesel (~> 0.1.5)
6 7
       rails (>= 3.0)
7 8
 
@@ -45,6 +46,7 @@ GEM
45 46
       cucumber (>= 1.1.1)
46 47
       ffi (>= 1.0.11)
47 48
       rspec (>= 2.7.0)
  49
+    bcrypt-ruby (3.0.1)
48 50
     bourne (1.1.2)
49 51
       mocha (= 0.10.5)
50 52
     builder (3.0.0)
6  NEWS.md
Source Rendered
... ...
@@ -1,3 +1,9 @@
  1
+HEAD:
  2
+
  3
+* Change default password strategy to BCrypt
  4
+* Provide BCryptMigrationFromSHA1 password strategy to help people migrate from
  5
+  SHA1 (the old default password strategy) to BCrypt (the new default).
  6
+
1 7
 New for 0.16.2:
2 8
 
3 9
 * Change default email sender to deploy@example.com .
23  README.md
Source Rendered
@@ -188,15 +188,27 @@ If you want to override the **model** behavior, you can include sub-modules of `
188 188
 Overriding the password strategy
189 189
 --------------------------------
190 190
 
191  
-By default, Clearance uses SHA1 encryption of the user's password. You can provide your own password strategy by creating a module that conforms to an API of two instance methods:
  191
+By default, Clearance uses BCrypt encryption of the user's password. You can provide your own password strategy by creating a module that conforms to an API of two instance methods:
192 192
 
193 193
     def authenticated?
194 194
     end
195 195
 
196  
-    def encrypt_password
  196
+    def password=(new_password)
197 197
     end
198 198
 
199  
-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.
  199
+The previous default password strategy was SHA1. To keep using SHA1, use this
  200
+code:
  201
+
  202
+    Clearance.configure do |config|
  203
+      config.password_strategy = Clearance::PasswordStrategies::SHA1
  204
+    end
  205
+
  206
+See [lib/clearance/password_strategies/bcrypt.rb](https://github.com/thoughtbot/clearance/blob/master/lib/clearance/password_strategies/bcrypt.rb) for the default behavior.
  207
+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.
  208
+Switching password strategies will cause your existing users' passwords to not
  209
+work. If you are currently using the SHA1 strategy (the previous default), and
  210
+want to transparently switch to BCrypt, use the [BCryptMigrationFromSHA1 strategy](https://github.com/thoughtbot/clearance/blob/master/lib/clearance/password_strategies/bcrypt_migration_from_sha1.rb).
  211
+
200 212
 
201 213
 Once you have an API-compliant module, load it with:
202 214
 
@@ -207,11 +219,14 @@ Once you have an API-compliant module, load it with:
207 219
 For example:
208 220
 
209 221
     # default
  222
+    config.password_strategy = Clearance::PasswordStrategies::BCrypt
  223
+    # use this strategy if you used to use SHA1, and now you want to use BCrypt
  224
+    config.password_strategy = Clearance::PasswordStrategies::BCryptMigrationFromSHA1
  225
+    # SHA1 (the previous default)
210 226
     config.password_strategy = Clearance::PasswordStrategies::SHA1
211 227
     # Blowfish
212 228
     config.password_strategy = Clearance::PasswordStrategies::Blowfish
213 229
 
214  
-
215 230
 Routing Constraints
216 231
 -------------------
217 232
 
1  clearance.gemspec
@@ -21,6 +21,7 @@ Gem::Specification.new do |s|
21 21
 
22 22
   s.add_dependency('rails',  '>= 3.0')
23 23
   s.add_dependency('diesel', '~> 0.1.5')
  24
+  s.add_dependency('bcrypt-ruby')
24 25
 
25 26
   s.add_development_dependency('bundler',        '~> 1.1')
26 27
   s.add_development_dependency('appraisal',      '~> 0.4.1')
2  features/engine/visitor_resets_password.feature
@@ -33,7 +33,7 @@ Feature: Password reset
33 33
     Then I should be signed in
34 34
 
35 35
   Scenario: User who was created before Clearance was installed creates password for first time
36  
-    Given a user "email@example.com" exists without a salt, remember token, or password
  36
+    Given a user "email@example.com" exists without a remember token or password
37 37
     When I reset the password for "email@example.com"
38 38
     When I follow the password reset link sent to "email@example.com"
39 39
     And I update my password with "newpassword"
4  features/step_definitions/engine/clearance_steps.rb
@@ -6,9 +6,9 @@
6 6
   FactoryGirl.create(:user, :email => email)
7 7
 end
8 8
 
9  
-Given /^a user "([^"]*)" exists without a salt, remember token, or password$/ do |email|
  9
+Given /^a user "([^"]*)" exists without a remember token or password$/ do |email|
10 10
   user = FactoryGirl.create(:user, :email => email)
11  
-  sql  = "update users set salt = NULL, encrypted_password = NULL, remember_token = NULL where id = #{user.id}"
  11
+  sql  = "update users set encrypted_password = NULL, remember_token = NULL where id = #{user.id}"
12 12
   ActiveRecord::Base.connection.update(sql)
13 13
 end
14 14
 
4  gemfiles/3.0.12.gemfile.lock
... ...
@@ -1,7 +1,8 @@
1 1
 PATH
2  
-  remote: /home/mike/clearance
  2
+  remote: /Users/gabe/thoughtbot/open-source/clearance
3 3
   specs:
4 4
     clearance (0.16.2)
  5
+      bcrypt-ruby
5 6
       diesel (~> 0.1.5)
6 7
       rails (>= 3.0)
7 8
 
@@ -45,6 +46,7 @@ GEM
45 46
       cucumber (>= 1.1.1)
46 47
       ffi (>= 1.0.11)
47 48
       rspec (>= 2.7.0)
  49
+    bcrypt-ruby (3.0.1)
48 50
     bourne (1.1.2)
49 51
       mocha (= 0.10.5)
50 52
     builder (2.1.2)
2  gemfiles/3.1.4.gemfile.lock
@@ -2,6 +2,7 @@ PATH
2 2
   remote: /Users/gabe/thoughtbot/open-source/clearance
3 3
   specs:
4 4
     clearance (0.16.2)
  5
+      bcrypt-ruby
5 6
       diesel (~> 0.1.5)
6 7
       rails (>= 3.0)
7 8
 
@@ -46,6 +47,7 @@ GEM
46 47
       cucumber (>= 1.1.1)
47 48
       ffi (>= 1.0.11)
48 49
       rspec (>= 2.7.0)
  50
+    bcrypt-ruby (3.0.1)
49 51
     bourne (1.1.2)
50 52
       mocha (= 0.10.5)
51 53
     builder (3.0.0)
2  gemfiles/3.2.3.gemfile.lock
@@ -2,6 +2,7 @@ PATH
2 2
   remote: /Users/gabe/thoughtbot/open-source/clearance
3 3
   specs:
4 4
     clearance (0.16.2)
  5
+      bcrypt-ruby
5 6
       diesel (~> 0.1.5)
6 7
       rails (>= 3.0)
7 8
 
@@ -45,6 +46,7 @@ GEM
45 46
       cucumber (>= 1.1.1)
46 47
       ffi (>= 1.0.11)
47 48
       rspec (>= 2.7.0)
  49
+    bcrypt-ruby (3.0.1)
48 50
     bourne (1.1.2)
49 51
       mocha (= 0.10.5)
50 52
     builder (3.0.0)
3  lib/clearance/password_strategies.rb
@@ -2,5 +2,8 @@ module Clearance
2 2
   module PasswordStrategies
3 3
     autoload :SHA1, 'clearance/password_strategies/sha1'
4 4
     autoload :Blowfish, 'clearance/password_strategies/blowfish'
  5
+    autoload :BCrypt, 'clearance/password_strategies/bcrypt'
  6
+    autoload :Fake, 'clearance/password_strategies/fake'
  7
+    autoload :BCryptMigrationFromSHA1, 'clearance/password_strategies/bcrypt_migration_from_sha1'
5 8
   end
6 9
 end
32  lib/clearance/password_strategies/bcrypt.rb
... ...
@@ -0,0 +1,32 @@
  1
+module Clearance
  2
+  module PasswordStrategies
  3
+    module BCrypt
  4
+      require 'bcrypt'
  5
+
  6
+      extend ActiveSupport::Concern
  7
+
  8
+      # Am I authenticated with given password?
  9
+      #
  10
+      # @param [String] plain-text password
  11
+      # @return [true, false]
  12
+      # @example
  13
+      #   user.authenticated?('password')
  14
+      def authenticated?(password)
  15
+        ::BCrypt::Password.new(encrypted_password) == password
  16
+      end
  17
+
  18
+      def password=(new_password)
  19
+        @password = new_password
  20
+        if new_password.present?
  21
+          self.encrypted_password = encrypt(new_password)
  22
+        end
  23
+      end
  24
+
  25
+      private
  26
+
  27
+      def encrypt(password)
  28
+        ::BCrypt::Password.create(password)
  29
+      end
  30
+    end
  31
+  end
  32
+end
52  lib/clearance/password_strategies/bcrypt_migration_from_sha1.rb
... ...
@@ -0,0 +1,52 @@
  1
+module Clearance
  2
+  module PasswordStrategies
  3
+    module BCryptMigrationFromSHA1
  4
+      class BCryptUser
  5
+        include Clearance::PasswordStrategies::BCrypt
  6
+
  7
+        def initialize(user)
  8
+          @user = user
  9
+        end
  10
+
  11
+        delegate :encrypted_password, :encrypted_password=, to: :@user
  12
+      end
  13
+
  14
+      class SHA1User
  15
+        include Clearance::PasswordStrategies::SHA1
  16
+
  17
+        def initialize(user)
  18
+          @user = user
  19
+        end
  20
+
  21
+        delegate :salt, :salt=, :encrypted_password, :encrypted_password=, to: :@user
  22
+      end
  23
+
  24
+      def authenticated?(password)
  25
+        authenticated_with_sha1?(password) || authenticated_with_bcrypt?(password)
  26
+      end
  27
+
  28
+      def password=(new_password)
  29
+        BCryptUser.new(self).password = new_password
  30
+      end
  31
+
  32
+      private
  33
+
  34
+      def authenticated_with_sha1?(password)
  35
+        if sha1_password?
  36
+          if SHA1User.new(self).authenticated?(password)
  37
+            self.password = password
  38
+            true
  39
+          end
  40
+        end
  41
+      end
  42
+
  43
+      def authenticated_with_bcrypt?(password)
  44
+        BCryptUser.new(self).authenticated?(password)
  45
+      end
  46
+
  47
+      def sha1_password?
  48
+        self.encrypted_password =~ /^[a-f0-9]{40}$/
  49
+      end
  50
+    end
  51
+  end
  52
+end
11  lib/clearance/password_strategies/blowfish.rb
@@ -13,15 +13,16 @@ def authenticated?(password)
13 13
         encrypted_password == encrypt(password)
14 14
       end
15 15
 
16  
-      protected
17  
-
18  
-      def encrypt_password
  16
+      def password=(new_password)
  17
+        @password = new_password
19 18
         initialize_salt_if_necessary
20  
-        if password.present?
21  
-          self.encrypted_password = encrypt(password)
  19
+        if new_password.present?
  20
+          self.encrypted_password = encrypt(new_password)
22 21
         end
23 22
       end
24 23
 
  24
+      protected
  25
+
25 26
       def generate_hash(string)
26 27
         cipher = OpenSSL::Cipher::Cipher.new('bf-cbc').encrypt
27 28
         cipher.key = Digest::SHA256.digest(salt)
34  lib/clearance/password_strategies/fake.rb
... ...
@@ -0,0 +1,34 @@
  1
+module Clearance
  2
+  module PasswordStrategies
  3
+    # The Clearance::PasswordStrategies::Fake module is meant to be used in test suites.
  4
+    # It stores passwords as plain text so your test suite doesn't pay the time cost
  5
+    # of any hashing algorithm.
  6
+    #
  7
+    # Use the fake in your test suite by requiring Clearance's testing helpers:
  8
+    #
  9
+    #     require 'clearance/testing'
  10
+    #
  11
+    # The usual places you'd require it are:
  12
+    #
  13
+    #     spec/support/clearance.rb
  14
+    #     features/support/clearance.rb
  15
+    module Fake
  16
+      extend ActiveSupport::Concern
  17
+
  18
+      def authenticated?(password)
  19
+        encrypted_password == password
  20
+      end
  21
+
  22
+      def password=(new_password)
  23
+        @password = new_password
  24
+        if new_password.present?
  25
+          self.encrypted_password = encrypt(password)
  26
+        end
  27
+      end
  28
+
  29
+      def encrypt(password)
  30
+        password
  31
+      end
  32
+    end
  33
+  end
  34
+end
17  lib/clearance/password_strategies/sha1.rb
... ...
@@ -1,8 +1,10 @@
1  
-require 'digest/sha1'
2  
-
3 1
 module Clearance
4 2
   module PasswordStrategies
5 3
     module SHA1
  4
+      require 'digest/sha1'
  5
+
  6
+      extend ActiveSupport::Concern
  7
+
6 8
       # Am I authenticated with given password?
7 9
       #
8 10
       # @param [String] plain-text password
@@ -13,15 +15,16 @@ def authenticated?(password)
13 15
         encrypted_password == encrypt(password)
14 16
       end
15 17
 
16  
-      protected
17  
-
18  
-      def encrypt_password
  18
+      def password=(new_password)
  19
+        @password = new_password
19 20
         initialize_salt_if_necessary
20  
-        if password.present?
21  
-          self.encrypted_password = encrypt(password)
  21
+        if new_password.present?
  22
+          self.encrypted_password = encrypt(new_password)
22 23
         end
23 24
       end
24 25
 
  26
+      private
  27
+
25 28
       def generate_hash(string)
26 29
         if RUBY_VERSION >= '1.9'
27 30
           Digest::SHA1.hexdigest(string).encode('UTF-8')
4  lib/clearance/testing.rb
@@ -2,6 +2,10 @@
2 2
 require 'clearance/testing/deny_access_matcher'
3 3
 require 'clearance/testing/helpers'
4 4
 
  5
+Clearance.configure do |config|
  6
+  config.password_strategy = Clearance::PasswordStrategies::Fake
  7
+end
  8
+
5 9
 if defined?(ActionController::TestCase)
6 10
   ActionController::TestCase.extend Clearance::Testing::Matchers
7 11
   class ActionController::TestCase
13  lib/clearance/user.rb
@@ -16,12 +16,12 @@ module User
16 16
     # @see Callbacks
17 17
     included do
18 18
       attr_accessor :password_changing
19  
-      attr_reader :password
  19
+      attr_reader   :password
20 20
 
21 21
       include Validations
22 22
       include Callbacks
23 23
 
24  
-      include (Clearance.configuration.password_strategy || Clearance::PasswordStrategies::SHA1)
  24
+      include (Clearance.configuration.password_strategy || Clearance::PasswordStrategies::BCrypt)
25 25
     end
26 26
 
27 27
     module ClassMethods
@@ -63,7 +63,7 @@ module Callbacks
63 63
       # salt, token, password encryption are handled before_save.
64 64
       included do
65 65
         before_validation :downcase_email
66  
-        before_create :generate_remember_token
  66
+        before_create     :generate_remember_token
67 67
       end
68 68
     end
69 69
 
@@ -108,12 +108,7 @@ def update_password(new_password)
108 108
       save
109 109
     end
110 110
 
111  
-    def password=(unencrypted_password)
112  
-      @password = unencrypted_password
113  
-      encrypt_password
114  
-    end
115  
-
116  
-    protected
  111
+    private
117 112
 
118 113
     def generate_random_code(length = 20)
119 114
       if RUBY_VERSION >= '1.9'
1  lib/generators/clearance/install/templates/db/migrate/upgrade_clearance_to_diesel.rb
@@ -5,7 +5,6 @@ def self.up
5 5
       columns = [
6 6
         [:email,              't.string :email'],
7 7
         [:encrypted_password, 't.string :encrypted_password, :limit => 128'],
8  
-        [:salt,               't.string :salt, :limit => 128'],
9 8
         [:confirmation_token, 't.string :confirmation_token, :limit => 128'],
10 9
         [:remember_token,     't.string :remember_token, :limit => 128']
11 10
       ].delete_if {|c| existing_columns.include?(c.first.to_s)}
13  spec/controllers/passwords_controller_spec.rb
@@ -98,21 +98,20 @@
98 98
 
99 99
     describe "on PUT to #update with password" do
100 100
       before do
101  
-        new_password = "new_password"
102  
-        @encrypted_new_password = @user.send(:encrypt, new_password)
103  
-        @user.encrypted_password.should_not == @encrypted_new_password
  101
+        @new_password = "new_password"
  102
+        @user.encrypted_password.should_not == @new_password
104 103
 
105 104
         put(:update,
106 105
             :user_id => @user,
107 106
             :token   => @user.confirmation_token,
108 107
             :user    => {
109  
-              :password => new_password
  108
+              :password => @new_password
110 109
             })
111 110
         @user.reload
112 111
       end
113 112
 
114 113
       it "should update password" do
115  
-        @user.encrypted_password.should == @encrypted_new_password
  114
+        @user.encrypted_password.should == @new_password
116 115
       end
117 116
 
118 117
       it "should clear confirmation token" do
@@ -137,8 +136,8 @@
137 136
         @user.reload
138 137
       end
139 138
 
140  
-      it "should not update password" do
141  
-        @user.encrypted_password.should_not == @encrypted_new_password
  139
+      it "should not update password to be blank" do
  140
+        @user.encrypted_password.should_not be_blank
142 141
       end
143 142
 
144 143
       it "should not clear token" do
72  spec/models/bcrypt_migration_from_sha1_spec.rb
... ...
@@ -0,0 +1,72 @@
  1
+require 'spec_helper'
  2
+
  3
+describe Clearance::PasswordStrategies::BCryptMigrationFromSHA1 do
  4
+  subject do
  5
+    Class.new do
  6
+      attr_accessor :encrypted_password
  7
+      attr_accessor :salt
  8
+      include Clearance::PasswordStrategies::BCryptMigrationFromSHA1
  9
+    end.new
  10
+  end
  11
+
  12
+  describe "#password=" do
  13
+    let(:salt)               { "salt" }
  14
+    let(:password)           { "password" }
  15
+    let(:encrypted_password) { stub("encrypted password") }
  16
+
  17
+    before do
  18
+      subject.salt = salt
  19
+      subject.encrypted_password = Digest::SHA1.hexdigest("--#{salt}--#{password}--")
  20
+
  21
+      BCrypt::Password.stubs(:create => encrypted_password)
  22
+      subject.password = password
  23
+    end
  24
+
  25
+    it "encrypts the password into a BCrypt-encrypted encrypted_password" do
  26
+      subject.encrypted_password.should == encrypted_password
  27
+    end
  28
+
  29
+    it "encrypts with BCrypt" do
  30
+      BCrypt::Password.should have_received(:create).with(password)
  31
+    end
  32
+  end
  33
+
  34
+  describe "#authenticated?" do
  35
+    let(:password)   { "password" }
  36
+    let(:salt)       { "salt" }
  37
+    let(:sha1_hash)  { Digest::SHA1.hexdigest("--#{salt}--#{password}--") }
  38
+
  39
+    context 'with a SHA1-encrypted password' do
  40
+      before do
  41
+        subject.salt = salt
  42
+        subject.encrypted_password = sha1_hash
  43
+      end
  44
+
  45
+      it "is authenticated" do
  46
+        subject.should be_authenticated(password)
  47
+      end
  48
+
  49
+      it "changes the hash into a BCrypt-encrypted one" do
  50
+        subject.authenticated?(password)
  51
+        subject.encrypted_password.should_not == sha1_hash
  52
+      end
  53
+    end
  54
+
  55
+    context "with a BCrypt-encrypted password" do
  56
+      let(:bcrypt_hash) { ::BCrypt::Password.create(password) }
  57
+
  58
+      before do
  59
+        subject.encrypted_password = bcrypt_hash
  60
+      end
  61
+
  62
+      it "is authenticated" do
  63
+        subject.should be_authenticated(password)
  64
+      end
  65
+
  66
+      it "does not change the hash" do
  67
+        subject.authenticated?(password)
  68
+        subject.encrypted_password.to_s.should == bcrypt_hash.to_s
  69
+      end
  70
+    end
  71
+  end
  72
+end
41  spec/models/bcrypt_spec.rb
... ...
@@ -0,0 +1,41 @@
  1
+require 'spec_helper'
  2
+
  3
+describe Clearance::PasswordStrategies::BCrypt do
  4
+  subject do
  5
+    Class.new do
  6
+      attr_accessor :encrypted_password
  7
+      include Clearance::PasswordStrategies::BCrypt
  8
+    end.new
  9
+  end
  10
+
  11
+  describe "#password=" do
  12
+    let(:password)           { "password" }
  13
+    let(:encrypted_password) { stub("encrypted password") }
  14
+
  15
+    before do
  16
+      BCrypt::Password.stubs(:create => encrypted_password)
  17
+
  18
+      subject.password = password
  19
+    end
  20
+
  21
+    it "encrypts the password into encrypted_password" do
  22
+      subject.encrypted_password.should == encrypted_password
  23
+    end
  24
+
  25
+    it "encrypts with BCrypt" do
  26
+      BCrypt::Password.should have_received(:create).with(password)
  27
+    end
  28
+  end
  29
+
  30
+  describe "#authenticated?" do
  31
+    let(:password) { "password" }
  32
+
  33
+    before do
  34
+      subject.password = password
  35
+    end
  36
+
  37
+    it "is authenticated with BCrypt" do
  38
+      subject.should be_authenticated(password)
  39
+    end
  40
+  end
  41
+end
15  spec/models/blowfish_spec.rb
@@ -3,14 +3,14 @@
3 3
 describe Clearance::PasswordStrategies::Blowfish do
4 4
   subject do
5 5
     Class.new do
6  
-      attr_accessor :salt, :password, :encrypted_password
  6
+      attr_accessor :salt, :encrypted_password
7 7
       include Clearance::PasswordStrategies::Blowfish
8 8
 
9 9
       def generate_random_code; "code"; end
10 10
     end.new
11 11
   end
12 12
 
13  
-  describe "#encrypt_password" do
  13
+  describe "#password=" do
14 14
     context "when the password is set" do
15 15
       let(:salt) { "salt" }
16 16
       let(:password) { "password" }
@@ -18,14 +18,17 @@ def generate_random_code; "code"; end
18 18
       before do
19 19
         subject.salt = salt
20 20
         subject.password = password
21  
-        subject.send(:encrypt_password)
22 21
       end
23 22
 
24  
-      it "should encrypt the password using Blowfish into encrypted_password" do
  23
+      it "doesn't initialize the salt" do
  24
+        subject.salt.should == salt
  25
+      end
  26
+
  27
+      it "encrypts the password using Blowfish into encrypted_password" do
25 28
         cipher = OpenSSL::Cipher::Cipher.new('bf-cbc').encrypt
26 29
         cipher.key = Digest::SHA256.digest(salt)
27 30
         expected = cipher.update("--#{salt}--#{password}--") << cipher.final
28  
-        
  31
+
29 32
         subject.encrypted_password.should == expected
30 33
       end
31 34
     end
@@ -34,7 +37,7 @@ def generate_random_code; "code"; end
34 37
       before do
35 38
         subject.salt = nil
36 39
 
37  
-        subject.send(:encrypt_password)
  40
+        subject.password = 'whatever'
38 41
       end
39 42
 
40 43
       it "should initialize the salt" do
4  spec/models/clearance_user_spec.rb → spec/models/password_strategies_spec.rb
@@ -26,8 +26,8 @@ def self.before_create(*args); end
26 26
   describe "when Clearance.configuration.password_strategy is not set" do
27 27
     before { Clearance.configuration.password_strategy = nil }
28 28
 
29  
-    it "includes Clearance::PasswordStrategies::SHA1" do
30  
-      subject.should be_kind_of(Clearance::PasswordStrategies::SHA1)
  29
+    it "includes Clearance::PasswordStrategies::BCrypt" do
  30
+      subject.should be_kind_of(Clearance::PasswordStrategies::BCrypt)
31 31
     end
32 32
   end
33 33
 end
28  spec/models/sha1_spec.rb
@@ -3,41 +3,47 @@
3 3
 describe Clearance::PasswordStrategies::SHA1 do
4 4
   subject do
5 5
     Class.new do
6  
-      attr_accessor :salt, :password, :encrypted_password
  6
+      attr_accessor :salt, :encrypted_password
7 7
       include Clearance::PasswordStrategies::SHA1
8 8
 
9 9
       def generate_random_code; "code"; end
10 10
     end.new
11 11
   end
12 12
 
13  
-  describe "#encrypt_password" do
  13
+  describe "#password=" do
14 14
     context "when the password is set" do
15  
-      let(:salt) { "salt" }
  15
+      let(:salt)     { "salt" }
16 16
       let(:password) { "password" }
17 17
 
18 18
       before do
19  
-        subject.salt = salt
  19
+        subject.salt     = salt
20 20
         subject.password = password
21  
-        subject.send(:encrypt_password)
22 21
       end
23 22
 
24  
-      it "should encrypt the password using SHA1 into encrypted_password" do
  23
+      it "doesn't initialize the salt" do
  24
+        subject.salt.should == salt
  25
+      end
  26
+
  27
+      it "encrypts the password using SHA1 and the existing salt into encrypted_password" do
25 28
         expected = Digest::SHA1.hexdigest("--#{salt}--#{password}--")
26 29
 
27 30
         subject.encrypted_password.should == expected
28 31
       end
29 32
     end
30 33
 
31  
-    context "when the salt is not set" do
  34
+    context "when the password is not set" do
32 35
       before do
33  
-        subject.salt = nil
34  
-
35  
-        subject.send(:encrypt_password)
  36
+        subject.salt     = nil
  37
+        subject.password = ""
36 38
       end
37 39
 
38  
-      it "should initialize the salt" do
  40
+      it "initializes the salt" do
39 41
         subject.salt.should_not be_nil
40 42
       end
  43
+
  44
+      it "doesn't encrpt the password" do
  45
+        subject.encrypted_password.should be_nil
  46
+      end
41 47
     end
42 48
   end
43 49
 end
9  spec/models/user_spec.rb
@@ -98,7 +98,7 @@
98 98
         @user.forgot_password!
99 99
       end
100 100
 
101  
-      it "should generate confirmation token" do
  101
+      it "generates confirmation token" do
102 102
         @user.confirmation_token.should_not be_nil
103 103
       end
104 104
 
@@ -108,11 +108,11 @@
108 108
             @user.update_password("new_password")
109 109
           end
110 110
 
111  
-          it "should change encrypted password" do
  111
+          it "changes encrypted password" do
112 112
             @user.encrypted_password.should_not == @old_encrypted_password
113 113
           end
114 114
 
115  
-          it "should clear confirmation token" do
  115
+          it "clears confirmation token" do
116 116
             @user.confirmation_token.should be_nil
117 117
           end
118 118
         end
@@ -182,9 +182,8 @@ def password_optional?
182 182
       @user.reload.remember_token.should be_nil
183 183
     end
184 184
 
185  
-    it "should initialize salt, generate remember token, and save encrypted password on update_password" do
  185
+    it "should generate remember token and save encrypted password on update_password" do
186 186
       @user.update_password('password')
187  
-      @user.salt.should_not be_nil
188 187
       @user.encrypted_password.should_not be_nil
189 188
       @user.remember_token.should_not be_nil
190 189
     end

0 notes on commit be37c35

Please sign in to comment.
Something went wrong with that request. Please try again.