Ticket #14390: patch_ticket14390.diff
File patch_ticket14390.diff, 9.5 KB (added by , 14 years ago) |
---|
-
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 2 2 import urllib 3 3 4 4 from django.contrib import auth 5 from django.contrib.auth.utils import (get_hexdigest, make_password, 6 check_password, is_password_usable) 5 7 from django.core.exceptions import ImproperlyConfigured 6 8 from django.db import models 7 9 from django.db.models.manager import EmptyManager 8 10 from django.contrib.contenttypes.models import ContentType 9 11 from django.utils.encoding import smart_str 10 from django.utils.hashcompat import md5_constructor, sha_constructor11 12 from django.utils.translation import ugettext_lazy as _ 12 13 13 14 14 UNUSABLE_PASSWORD = '!' # This will never be a valid hash15 16 def get_hexdigest(algorithm, salt, raw_password):17 """18 Returns a string of the hexdigest of the given plaintext password and salt19 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 crypt25 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. Handles38 encryption formats behind the scenes.39 """40 algo, salt, hsh = enc_password.split('$')41 return hsh == get_hexdigest(algo, salt, raw_password)42 43 15 class SiteProfileNotAvailable(Exception): 44 16 pass 45 17 … … class User(models.Model): 234 206 return full_name.strip() 235 207 236 208 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) 245 210 246 211 def check_password(self, raw_password): 247 212 """ … … class User(models.Model): 261 226 262 227 def set_unusable_password(self): 263 228 # Sets a value that will never be a valid hash 264 self.password = UNUSABLE_PASSWORD229 self.password = make_password('sha1', None) 265 230 266 231 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) 272 233 273 234 def get_group_permissions(self, obj=None): 274 235 """ -
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 1 1 from django.contrib.auth.tests.auth_backends import BackendTest, RowlevelBackendTest, AnonymousUserBackendTest, NoAnonymousUserBackendTest 2 from django.contrib.auth.tests.basic import BasicTestCase 2 from django.contrib.auth.tests.basic import BasicTestCase, PasswordUtilsTestCase 3 3 from django.contrib.auth.tests.decorators import LoginRequiredTestCase 4 4 from django.contrib.auth.tests.forms import UserCreationFormTest, AuthenticationFormTest, SetPasswordFormTest, PasswordChangeFormTest, UserChangeFormTest, PasswordResetFormTest 5 5 from 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 1 1 from django.test import TestCase 2 from django.utils.unittest import skipUnless 2 3 from django.contrib.auth.models import User, AnonymousUser 4 from django.contrib.auth import utils 3 5 from django.core.management import call_command 4 6 from StringIO import StringIO 5 7 8 try: 9 import crypt as crypt_module 10 except ImportError: 11 crypt_module = None 12 13 class 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 6 40 class BasicTestCase(TestCase): 7 41 def test_user(self): 8 42 "Check that users can be created and can set their password" -
new file django/contrib/auth/utils.py
diff --git a/django/contrib/auth/utils.py b/django/contrib/auth/utils.py new file mode 100644 index 0000000..b122dd6
- + 1 from django.utils.encoding import smart_str 2 from django.utils.hashcompat import md5_constructor, sha_constructor 3 import random 4 5 6 UNUSABLE_PASSWORD = '!' # This will never be a valid hash 7 8 9 def get_hexdigest(algorithm, salt, raw_password): 10 """ 11 Returns a string of the hexdigest of the given plaintext password and salt 12 using the given algorithm ('md5', 'sha1' or 'crypt'). 13 """ 14 raw_password, salt = smart_str(raw_password), smart_str(salt) 15 if algorithm == 'crypt': 16 try: 17 import crypt 18 except ImportError: 19 raise ValueError('"crypt" password algorithm not supported in this environment') 20 return crypt.crypt(raw_password, salt) 21 if algorithm == 'md5': 22 return md5_constructor(salt + raw_password).hexdigest() 23 elif algorithm == 'sha1': 24 return sha_constructor(salt + raw_password).hexdigest() 25 raise ValueError("Got unknown password algorithm type in password.") 26 27 28 def check_password(raw_password, enc_password): 29 """ 30 Returns a boolean of whether the raw_password was correct. Handles 31 encryption formats behind the scenes. 32 """ 33 parts = enc_password.split('$') 34 if len(parts) != 3: 35 return False 36 algo, salt, hsh = parts 37 return hsh == get_hexdigest(algo, salt, raw_password) 38 39 40 def is_password_usable(enc_password): 41 return enc_password is not None and enc_password != UNUSABLE_PASSWORD 42 43 44 def make_password(algo, raw_password): 45 """ 46 Produce a new password string in this format: algorithm$salt$hash 47 """ 48 if raw_password is None: 49 return UNUSABLE_PASSWORD 50 salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5] 51 hsh = get_hexdigest(algo, salt, raw_password) 52 return '%s$%s$%s' % (algo, salt, hsh) -
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`: 621 621 622 622 .. _backends documentation: #other-authentication-sources 623 623 624 Manually checking auser's password624 Manually managing user's password 625 625 ----------------------------------- 626 626 627 .. versionchanged:: 1.3 628 629 Function :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 631 will still work, but you should update your imports. 632 633 .. versionadded:: 1.3 634 635 The :mod:`django.contrib.auth.utils` module provides a set of functions to 636 create and validate hashed password. You can use them independently from 637 the ``User`` model. 638 627 639 .. function:: check_password() 628 640 629 641 If you'd like to manually authenticate a user by comparing a plain-text 630 642 password to the hashed password in the database, use the convenience 631 function :func:`django.contrib.auth. models.check_password`. It takes two643 function :func:`django.contrib.auth.utils.check_password`. It takes two 632 644 arguments: the plain-text password to check, and the full value of a user's 633 645 ``password`` field in the database to check against, and returns ``True`` 634 646 if they match, ``False`` otherwise. 635 647 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 636 663 How to log a user out 637 664 --------------------- 638 665