Author: PaulM
Date: 2012-02-29 12:12:16 -0800 (Wed, 29 Feb 2012)
New Revision: 17604

Modified:
   django/trunk/django/conf/global_settings.py
   django/trunk/django/contrib/auth/hashers.py
   django/trunk/django/contrib/auth/tests/hashers.py
Log:
Fixes #17777 and makes tests run again.

Adds a salted MD5 hasher for backwards compatibility.
Thanks [email protected] for the report.

Also fixes a bug preventing the hasher tests from being run during
contrib tests.


Modified: django/trunk/django/conf/global_settings.py
===================================================================
--- django/trunk/django/conf/global_settings.py 2012-02-29 17:46:23 UTC (rev 
17603)
+++ django/trunk/django/conf/global_settings.py 2012-02-29 20:12:16 UTC (rev 
17604)
@@ -507,6 +507,7 @@
     'django.contrib.auth.hashers.BCryptPasswordHasher',
     'django.contrib.auth.hashers.SHA1PasswordHasher',
     'django.contrib.auth.hashers.MD5PasswordHasher',
+    'django.contrib.auth.hashers.UnsaltedMD5PasswordHasher',
     'django.contrib.auth.hashers.CryptPasswordHasher',
 )
 

Modified: django/trunk/django/contrib/auth/hashers.py
===================================================================
--- django/trunk/django/contrib/auth/hashers.py 2012-02-29 17:46:23 UTC (rev 
17603)
+++ django/trunk/django/contrib/auth/hashers.py 2012-02-29 20:12:16 UTC (rev 
17604)
@@ -36,7 +36,7 @@
     encoded = smart_str(encoded)
 
     if len(encoded) == 32 and '$' not in encoded:
-        hasher = get_hasher('md5')
+        hasher = get_hasher('unsalted_md5')
     else:
         algorithm = encoded.split('$', 1)[0]
         hasher = get_hasher(algorithm)
@@ -69,11 +69,13 @@
     return hasher.encode(password, salt)
 
 
-def load_hashers():
+def load_hashers(password_hashers=None):
     global HASHERS
     global PREFERRED_HASHER
     hashers = []
-    for backend in settings.PASSWORD_HASHERS:
+    if not password_hashers:
+        password_hashers = settings.PASSWORD_HASHERS
+    for backend in password_hashers:
         try:
             mod_path, cls_name = backend.rsplit('.', 1)
             mod = importlib.import_module(mod_path)
@@ -301,6 +303,34 @@
 
 class MD5PasswordHasher(BasePasswordHasher):
     """
+    The Salted MD5 password hashing algorithm (not recommended)
+    """
+    algorithm = "md5"
+
+    def encode(self, password, salt):
+        assert password
+        assert salt and '$' not in salt
+        hash = hashlib.md5(salt + password).hexdigest()
+        return "%s$%s$%s" % (self.algorithm, salt, hash)
+
+    def verify(self, password, encoded):
+        algorithm, salt, hash = encoded.split('$', 2)
+        assert algorithm == self.algorithm
+        encoded_2 = self.encode(password, salt)
+        return constant_time_compare(encoded, encoded_2)
+
+    def safe_summary(self, encoded):
+        algorithm, salt, hash = encoded.split('$', 2)
+        assert algorithm == self.algorithm
+        return SortedDict([
+            (_('algorithm'), algorithm),
+            (_('salt'), mask_hash(salt, show=2)),
+            (_('hash'), mask_hash(hash)),
+        ])
+
+
+class UnsaltedMD5PasswordHasher(BasePasswordHasher):
+    """
     I am an incredibly insecure algorithm you should *never* use;
     stores unsalted MD5 hashes without the algorithm prefix.
 
@@ -308,7 +338,7 @@
     this way. Some older Django installs still have these values
     lingering around so we need to handle and upgrade them properly.
     """
-    algorithm = "md5"
+    algorithm = "unsalted_md5"
 
     def salt(self):
         return ''

Modified: django/trunk/django/contrib/auth/tests/hashers.py
===================================================================
--- django/trunk/django/contrib/auth/tests/hashers.py   2012-02-29 17:46:23 UTC 
(rev 17603)
+++ django/trunk/django/contrib/auth/tests/hashers.py   2012-02-29 20:12:16 UTC 
(rev 17604)
@@ -20,7 +20,7 @@
 
 class TestUtilsHashPass(unittest.TestCase):
     def setUp(self):
-        load_hashers()
+        load_hashers(password_hashers=default_hashers)
 
     def test_simple(self):
         encoded = make_password('letmein')
@@ -47,6 +47,14 @@
 
     def test_md5(self):
         encoded = make_password('letmein', 'seasalt', 'md5')
+        self.assertEqual(encoded, 
+                         'md5$seasalt$f5531bef9f3687d0ccf0f617f0e25573')
+        self.assertTrue(is_password_usable(encoded))
+        self.assertTrue(check_password(u'letmein', encoded))
+        self.assertFalse(check_password('letmeinz', encoded))
+
+    def test_unsalted_md5(self):
+        encoded = make_password('letmein', 'seasalt', 'unsalted_md5')
         self.assertEqual(encoded, '0d107d09f5bbe40cade3de5c71e9e9b7')
         self.assertTrue(is_password_usable(encoded))
         self.assertTrue(check_password(u'letmein', encoded))
@@ -123,6 +131,3 @@
                 state['upgraded'] = True
             self.assertFalse(check_password('WRONG', encoded, setter))
             self.assertFalse(state['upgraded'])
-
-
-TestUtilsHashPass = 
override_settings(PASSWORD_HASHERS=default_hashers)(TestUtilsHashPass)

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en.

Reply via email to