Upgrade passwords to the latest hashing scheme on login
authorTom Hughes <tom@compton.nu>
Tue, 13 Aug 2013 23:13:49 +0000 (00:13 +0100)
committerTom Hughes <tom@compton.nu>
Tue, 13 Aug 2013 23:24:33 +0000 (00:24 +0100)
app/models/user.rb
lib/password_hash.rb
test/unit/password_hash_test.rb

index 4c51089..b8ec9ae 100644 (file)
@@ -70,7 +70,14 @@ class User < ActiveRecord::Base
         end
       end
 
-      user = nil if user and not PasswordHash.check(user.pass_crypt, user.pass_salt, options[:password])
+      if user and PasswordHash.check(user.pass_crypt, user.pass_salt, options[:password])
+        if PasswordHash.upgrade?(user.pass_crypt, user.pass_salt)
+          user.pass_crypt, user.pass_salt = PasswordHash.create(options[:password])
+          user.save
+        end
+      else
+        user = nil
+      end
     elsif options[:token]
       token = UserToken.find_by_token(options[:token])
       user = token.user if token
index 1bd8029..5adfc7a 100644 (file)
@@ -29,6 +29,22 @@ module PasswordHash
     return hash == candidate
   end
 
+  def self.upgrade?(hash, salt)
+    if salt.nil?
+      return true
+    elsif salt =~ /!/
+      algorithm, iterations, salt = salt.split("!")
+      return true if Base64.strict_decode64(salt).length != SALT_BYTE_SIZE
+      return true if Base64.strict_decode64(hash).length != HASH_BYTE_SIZE
+      return true if iterations.to_i != PBKDF2_ITERATIONS
+      return true if algorithm != DIGEST_ALGORITHM
+    else
+      return true
+    end
+
+    return false
+  end
+
 private
 
   def self.hash(password, salt, iterations, size, algorithm)
index 61d3d49..825942a 100644 (file)
@@ -4,12 +4,14 @@ class PasswordHashTest < ActiveSupport::TestCase
   def test_md5_without_salt
     assert_equal true, PasswordHash.check("5f4dcc3b5aa765d61d8327deb882cf99", nil, "password")
     assert_equal false, PasswordHash.check("5f4dcc3b5aa765d61d8327deb882cf99", nil, "wrong")
+    assert_equal true, PasswordHash.upgrade?("5f4dcc3b5aa765d61d8327deb882cf99", nil)
   end
 
   def test_md5_with_salt
     assert_equal true, PasswordHash.check("67a1e09bb1f83f5007dc119c14d663aa", "salt", "password")
     assert_equal false, PasswordHash.check("67a1e09bb1f83f5007dc119c14d663aa", "salt", "wrong")
     assert_equal false, PasswordHash.check("67a1e09bb1f83f5007dc119c14d663aa", "wrong", "password")
+    assert_equal true, PasswordHash.upgrade?("67a1e09bb1f83f5007dc119c14d663aa", "salt")
   end
 
   def test_default
@@ -21,5 +23,7 @@ class PasswordHashTest < ActiveSupport::TestCase
     assert_equal false, PasswordHash.check(hash1, salt1, "wrong")
     assert_equal true, PasswordHash.check(hash2, salt2, "password")
     assert_equal false, PasswordHash.check(hash2, salt2, "wrong")
+    assert_equal false, PasswordHash.upgrade?(hash1, salt1)
+    assert_equal false, PasswordHash.upgrade?(hash2, salt2)
   end
 end