Commit 703c2666 authored by Claude Paroz's avatar Claude Paroz
Browse files

Fixed #18182 -- Made is_usable_password check if hashing algorithm is correct

The display of the ReadOnlyPasswordHashWidget has also been improved to
distinguish empty/unusable password from erroneous password.
Fixed #18453 also.
Thanks danielr and Leo for the reports and Moritz Sichert for the
initial patch.
parent 859aa2a6
Loading
Loading
Loading
Loading
+14 −14
Original line number Diff line number Diff line
@@ -11,7 +11,7 @@ from django.utils.translation import ugettext, ugettext_lazy as _

from django.contrib.auth import authenticate
from django.contrib.auth.models import User
from django.contrib.auth.hashers import UNUSABLE_PASSWORD, is_password_usable, identify_hasher
from django.contrib.auth.hashers import UNUSABLE_PASSWORD, identify_hasher
from django.contrib.auth.tokens import default_token_generator
from django.contrib.sites.models import get_current_site

@@ -24,16 +24,16 @@ mask_password = lambda p: "%s%s" % (p[:UNMASKED_DIGITS_TO_SHOW], "*" * max(len(p
class ReadOnlyPasswordHashWidget(forms.Widget):
    def render(self, name, value, attrs):
        encoded = value

        if not is_password_usable(encoded):
            return "None"

        final_attrs = self.build_attrs(attrs)

        if encoded == '' or encoded == UNUSABLE_PASSWORD:
            summary = mark_safe("<strong>%s</strong>" % ugettext("No password set."))
        else:
            try:
                hasher = identify_hasher(encoded)
            except ValueError:
            summary = mark_safe("<strong>Invalid password format or unknown hashing algorithm.</strong>")
                summary = mark_safe("<strong>%s</strong>" % ugettext(
                    "Invalid password format or unknown hashing algorithm."))
            else:
                summary = format_html_join('',
                                           "<strong>{0}</strong>: {1} ",
+7 −1
Original line number Diff line number Diff line
@@ -28,7 +28,13 @@ def reset_hashers(**kwargs):


def is_password_usable(encoded):
    return (encoded is not None and encoded != UNUSABLE_PASSWORD)
    if encoded is None or encoded == UNUSABLE_PASSWORD:
        return False
    try:
        hasher = identify_hasher(encoded)
    except ValueError:
        return False
    return True


def check_password(password, encoded, setter=None, preferred='default'):
+13 −6
Original line number Diff line number Diff line
@@ -236,23 +236,30 @@ class UserChangeFormTest(TestCase):
        # Just check we can create it
        form = MyUserForm({})

    def test_unsuable_password(self):
        user = User.objects.get(username='empty_password')
        user.set_unusable_password()
        user.save()
        form = UserChangeForm(instance=user)
        self.assertIn(_("No password set."), form.as_table())

    def test_bug_17944_empty_password(self):
        user = User.objects.get(username='empty_password')
        form = UserChangeForm(instance=user)
        # Just check that no error is raised.
        form.as_table()
        self.assertIn(_("Invalid password format or unknown hashing algorithm."),
            form.as_table())

    def test_bug_17944_unmanageable_password(self):
        user = User.objects.get(username='unmanageable_password')
        form = UserChangeForm(instance=user)
        # Just check that no error is raised.
        form.as_table()
        self.assertIn(_("Invalid password format or unknown hashing algorithm."),
            form.as_table())

    def test_bug_17944_unknown_password_algorithm(self):
        user = User.objects.get(username='unknown_password')
        form = UserChangeForm(instance=user)
        # Just check that no error is raised.
        form.as_table()
        self.assertIn(_("Invalid password format or unknown hashing algorithm."),
            form.as_table())


@override_settings(USE_TZ=False, PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
+4 −0
Original line number Diff line number Diff line
@@ -100,6 +100,10 @@ class TestUtilsHashPass(unittest.TestCase):
        self.assertRaises(ValueError, doit)
        self.assertRaises(ValueError, identify_hasher, "lolcat$salt$hash")

    def test_bad_encoded(self):
        self.assertFalse(is_password_usable('letmein_badencoded'))
        self.assertFalse(is_password_usable(''))

    def test_low_level_pkbdf2(self):
        hasher = PBKDF2PasswordHasher()
        encoded = hasher.encode('letmein', 'seasalt')