Ticket #12669: patch_ticket14390.diff

File patch_ticket14390.diff, 7.6 KB (added by Łukasz Rekucki, 14 years ago)

Patch with docs and tests.

  • django/contrib/auth/models.py

    diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py
    index 16a8b99..d74ca05 100644
    a b import datetime  
    22import urllib
    33
    44from django.contrib import auth
     5from django.contrib.auth.utils import (get_hexdigest, make_password,
     6                                    check_password, is_password_usable)
    57from django.core.exceptions import ImproperlyConfigured
    68from django.db import models
    79from django.db.models.manager import EmptyManager
    810from django.contrib.contenttypes.models import ContentType
    911from django.utils.encoding import smart_str
    10 from django.utils.hashcompat import md5_constructor, sha_constructor
    1112from django.utils.translation import ugettext_lazy as _
    1213
    1314
    14 UNUSABLE_PASSWORD = '!' # This will never be a valid hash
    15 
    16 def get_hexdigest(algorithm, salt, raw_password):
    17     """
    18     Returns a string of the hexdigest of the given plaintext password and salt
    19     using the given algorithm ('md5', 'sha1' or 'crypt').
    20     """
    21     raw_password, salt = smart_str(raw_password), smart_str(salt)
    22     if algorithm == 'crypt':
    23         try:
    24             import crypt
    25         except ImportError:
    26             raise ValueError('"crypt" password algorithm not supported in this environment')
    27         return crypt.crypt(raw_password, salt)
    28 
    29     if algorithm == 'md5':
    30         return md5_constructor(salt + raw_password).hexdigest()
    31     elif algorithm == 'sha1':
    32         return sha_constructor(salt + raw_password).hexdigest()
    33     raise ValueError("Got unknown password algorithm type in password.")
    34 
    35 def check_password(raw_password, enc_password):
    36     """
    37     Returns a boolean of whether the raw_password was correct. Handles
    38     encryption formats behind the scenes.
    39     """
    40     algo, salt, hsh = enc_password.split('$')
    41     return hsh == get_hexdigest(algo, salt, raw_password)
    42 
    4315class SiteProfileNotAvailable(Exception):
    4416    pass
    4517
    class User(models.Model):  
    234206        return full_name.strip()
    235207
    236208    def set_password(self, raw_password):
    237         if raw_password is None:
    238             self.set_unusable_password()
    239         else:
    240             import random
    241             algo = 'sha1'
    242             salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
    243             hsh = get_hexdigest(algo, salt, raw_password)
    244             self.password = '%s$%s$%s' % (algo, salt, hsh)
     209        self.password = make_password('sha1', raw_password)
    245210
    246211    def check_password(self, raw_password):
    247212        """
    class User(models.Model):  
    261226
    262227    def set_unusable_password(self):
    263228        # Sets a value that will never be a valid hash
    264         self.password = UNUSABLE_PASSWORD
     229        self.password = make_password('sha1', None)
    265230
    266231    def has_usable_password(self):
    267         if self.password is None \
    268             or self.password == UNUSABLE_PASSWORD:
    269             return False
    270         else:
    271             return True
     232        return is_password_usable(self.password)
    272233
    273234    def get_group_permissions(self, obj=None):
    274235        """
  • django/contrib/auth/tests/__init__.py

    diff --git a/django/contrib/auth/tests/__init__.py b/django/contrib/auth/tests/__init__.py
    index 98061a1..75dd415 100644
    a b  
    11from django.contrib.auth.tests.auth_backends import BackendTest, RowlevelBackendTest, AnonymousUserBackendTest, NoAnonymousUserBackendTest
    2 from django.contrib.auth.tests.basic import BasicTestCase
     2from django.contrib.auth.tests.basic import BasicTestCase, PasswordUtilsTestCase
    33from django.contrib.auth.tests.decorators import LoginRequiredTestCase
    44from django.contrib.auth.tests.forms import UserCreationFormTest, AuthenticationFormTest, SetPasswordFormTest, PasswordChangeFormTest, UserChangeFormTest, PasswordResetFormTest
    55from django.contrib.auth.tests.remote_user \
  • django/contrib/auth/tests/basic.py

    diff --git a/django/contrib/auth/tests/basic.py b/django/contrib/auth/tests/basic.py
    index 7493dc6..6fb8d1b 100644
    a b  
    11from django.test import TestCase
     2from django.utils.unittest import skipUnless
    23from django.contrib.auth.models import User, AnonymousUser
     4from django.contrib.auth import utils
    35from django.core.management import call_command
    46from StringIO import StringIO
    57
     8try:
     9    import crypt as crypt_module
     10except ImportError:
     11    crypt_module = None
     12
     13class PasswordUtilsTestCase(TestCase):
     14
     15    def _test_make_password(self, algo):
     16        password = utils.make_password(algo, "foobar")
     17        self.assertTrue(utils.is_password_usable(password))
     18        self.assertTrue(utils.check_password("foobar", password))
     19
     20    def test_make_unusable(self):
     21        "Check that you can create an unusable password."
     22        password = utils.make_password("any", None)
     23        self.assertFalse(utils.is_password_usable(password))
     24        self.assertFalse(utils.check_password("foobar", password))
     25
     26    def test_make_password_sha1(self):
     27        "Check creating passwords with SHA1 algorithm."
     28        self._test_make_password("sha1")
     29
     30    def test_make_password_md5(self):
     31        "Check creating passwords with MD5 algorithm."
     32        self._test_make_password("md5")
     33
     34    @skipUnless(crypt_module, "no crypt module to generate password.")
     35    def test_make_password_crypt(self):
     36        "Check creating passwords with CRYPT algorithm."
     37        self._test_make_password("crypt")
     38
     39
    640class BasicTestCase(TestCase):
    741    def test_user(self):
    842        "Check that users can be created and can set their password"
  • docs/topics/auth.txt

    diff --git a/docs/topics/auth.txt b/docs/topics/auth.txt
    index e0856e8..0c3ad56 100644
    a b Django provides two functions in :mod:`django.contrib.auth`:  
    621621
    622622.. _backends documentation: #other-authentication-sources
    623623
    624 Manually checking a user's password
     624Manually managing user's password
    625625-----------------------------------
    626626
     627.. versionchanged:: 1.3
     628
     629Function :func:`django.contrib.auth.models.check_password` has been moved to the
     630:mod:`django.contrib.auth.utils` module. Importing it from the old location
     631will still work, but you should update your imports.
     632
     633.. versionadded:: 1.3
     634
     635The :mod:`django.contrib.auth.utils` module provides a set of functions to
     636create and validate hashed password. You can use them independently from
     637the ``User`` model.
     638
    627639.. function:: check_password()
    628640
    629641    If you'd like to manually authenticate a user by comparing a plain-text
    630642    password to the hashed password in the database, use the convenience
    631     function :func:`django.contrib.auth.models.check_password`. It takes two
     643    function :func:`django.contrib.auth.utils.check_password`. It takes two
    632644    arguments: the plain-text password to check, and the full value of a user's
    633645    ``password`` field in the database to check against, and returns ``True``
    634646    if they match, ``False`` otherwise.
    635647
     648.. function:: make_password()
     649
     650    Creates a hashed password in the format used by this application. It takes
     651    two arguments: hashing algorithm to use and the password in plain-text.
     652    Currently supported algorithms are: `sha1`, `md5` and `crypt` if you have
     653    the `crypt` library installed. If the second argument is ``None``, an
     654    unusable password is returned (a one that will be never accepted by
     655    :func:`django.contrib.auth.utils.check_password`).
     656
     657.. function:: is_password_usable()
     658
     659    Checks if the given string is a hashed password that has a chance
     660    of being verified against :func:`django.contrib.auth.utils.check_password`.
     661
     662
    636663How to log a user out
    637664---------------------
    638665
Back to Top