Ticket #18184: identify_hasher.patch

File identify_hasher.patch, 5.7 KB (added by Eli Collins <elic@…>, 12 years ago)

patch implementing change, with unittests for new function

  • django/contrib/auth/hashers.py

     
    116116        return HASHERS[algorithm]
    117117
    118118
     119def identify_hasher(encoded):
     120    """
     121    Returns an instance of a loaded password hasher.
     122
     123    Identifies hasher algorithm by examining encoded hash, and calls
     124    get_hasher() to return hasher. Raises ValueError if
     125    algorithm cannot be identified, or if hasher is not loaded.
     126    """
     127    encoded = smart_str(encoded)
     128    if len(encoded) == 32 and '$' not in encoded:
     129        algorithm = 'unsalted_md5'
     130    else:
     131        algorithm = encoded.split('$', 1)[0]
     132    return get_hasher(algorithm)
     133
     134
    119135def mask_hash(hash, show=6, char="*"):
    120136    """
    121137    Returns the given hash, with only the first ``show`` number shown. The
  • django/contrib/auth/tests/hashers.py

     
    11from django.conf.global_settings import PASSWORD_HASHERS as default_hashers
    22from django.contrib.auth.hashers import (is_password_usable,
    33    check_password, make_password, PBKDF2PasswordHasher, load_hashers,
    4     PBKDF2SHA1PasswordHasher, get_hasher, UNUSABLE_PASSWORD)
     4    PBKDF2SHA1PasswordHasher, get_hasher, identify_hasher, UNUSABLE_PASSWORD)
    55from django.utils import unittest
    66from django.utils.unittest import skipUnless
    77
     
    3636        self.assertTrue(is_password_usable(encoded))
    3737        self.assertTrue(check_password(u'letmein', encoded))
    3838        self.assertFalse(check_password('letmeinz', encoded))
     39        self.assertEqual(identify_hasher(encoded).algorithm, "pbkdf2_sha256")
    3940
    4041    def test_sha1(self):
    4142        encoded = make_password('letmein', 'seasalt', 'sha1')
     
    4445        self.assertTrue(is_password_usable(encoded))
    4546        self.assertTrue(check_password(u'letmein', encoded))
    4647        self.assertFalse(check_password('letmeinz', encoded))
     48        self.assertEqual(identify_hasher(encoded).algorithm, "sha1")
    4749
    4850    def test_md5(self):
    4951        encoded = make_password('letmein', 'seasalt', 'md5')
     
    5254        self.assertTrue(is_password_usable(encoded))
    5355        self.assertTrue(check_password(u'letmein', encoded))
    5456        self.assertFalse(check_password('letmeinz', encoded))
     57        self.assertEqual(identify_hasher(encoded).algorithm, "md5")
    5558
    5659    def test_unsalted_md5(self):
    5760        encoded = make_password('letmein', 'seasalt', 'unsalted_md5')
     
    5962        self.assertTrue(is_password_usable(encoded))
    6063        self.assertTrue(check_password(u'letmein', encoded))
    6164        self.assertFalse(check_password('letmeinz', encoded))
     65        self.assertEqual(identify_hasher(encoded).algorithm, "unsalted_md5")
    6266
    6367    @skipUnless(crypt, "no crypt module to generate password.")
    6468    def test_crypt(self):
     
    6771        self.assertTrue(is_password_usable(encoded))
    6872        self.assertTrue(check_password(u'letmein', encoded))
    6973        self.assertFalse(check_password('letmeinz', encoded))
     74        self.assertEqual(identify_hasher(encoded).algorithm, "crypt")
    7075
    7176    @skipUnless(bcrypt, "py-bcrypt not installed")
    7277    def test_bcrypt(self):
     
    7580        self.assertTrue(encoded.startswith('bcrypt$'))
    7681        self.assertTrue(check_password(u'letmein', encoded))
    7782        self.assertFalse(check_password('letmeinz', encoded))
     83        self.assertEqual(identify_hasher(encoded).algorithm, "bcrypt")
    7884
    7985    def test_unusable(self):
    8086        encoded = make_password(None)
     
    8490        self.assertFalse(check_password('', encoded))
    8591        self.assertFalse(check_password(u'letmein', encoded))
    8692        self.assertFalse(check_password('letmeinz', encoded))
     93        self.assertRaises(ValueError, identify_hasher, encoded)
    8794
    8895    def test_bad_algorithm(self):
    8996        def doit():
    9097            make_password('letmein', hasher='lolcat')
    9198        self.assertRaises(ValueError, doit)
     99        self.assertRaises(ValueError, identify_hasher, "lolcat$salt$hash")
    92100
    93101    def test_low_level_pkbdf2(self):
    94102        hasher = PBKDF2PasswordHasher()
  • django/contrib/auth/forms.py

     
    88
    99from django.contrib.auth import authenticate
    1010from django.contrib.auth.models import User
    11 from django.contrib.auth.hashers import UNUSABLE_PASSWORD, is_password_usable, get_hasher
     11from django.contrib.auth.hashers import UNUSABLE_PASSWORD, is_password_usable, identify_hasher
    1212from django.contrib.auth.tokens import default_token_generator
    1313from django.contrib.sites.models import get_current_site
    1414
     
    2626
    2727        final_attrs = self.build_attrs(attrs)
    2828
    29         encoded = smart_str(encoded)
    30 
    31         if len(encoded) == 32 and '$' not in encoded:
    32             algorithm = 'unsalted_md5'
    33         else:
    34             algorithm = encoded.split('$', 1)[0]
    35 
    3629        try:
    37             hasher = get_hasher(algorithm)
     30            hasher = identify_hasher(encoded)
    3831        except ValueError:
    3932            summary = "<strong>Invalid password format or unknown hashing algorithm.</strong>"
    4033        else:
    4134            summary = ""
    42             for key, value in hasher.safe_summary(encoded).iteritems():
     35            for key, value in hasher.safe_summary(smart_str(encoded)).iteritems():
    4336                summary += "<strong>%(key)s</strong>: %(value)s " % {"key": ugettext(key), "value": value}
    4437
    4538        return mark_safe("<div%(attrs)s>%(summary)s</div>" % {"attrs": flatatt(final_attrs), "summary": summary})
Back to Top