Fixed #14354 -- Normalized the handling of empty/null passwords in contrib.auth. This also updates the createsuperuser command to be more testable, and migrates some auth doctests. Thanks to berryp for the report, and Laurent Luce for the patch.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@14053 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
71a4c472ab
commit
8755fb1549
1
AUTHORS
1
AUTHORS
|
@ -310,6 +310,7 @@ answer newbie questions, and generally made Django that much better:
|
|||
Simon Litchfield <simon@quo.com.au>
|
||||
Daniel Lindsley <polarcowz@gmail.com>
|
||||
Trey Long <trey@ktrl.com>
|
||||
Laurent Luce <http://www.laurentluce.com>
|
||||
Martin Mahner <http://www.mahner.org/>
|
||||
Matt McClanahan <http://mmcc.cx/>
|
||||
Stanislaus Madueke
|
||||
|
|
|
@ -41,7 +41,8 @@ class Command(BaseCommand):
|
|||
username = options.get('username', None)
|
||||
email = options.get('email', None)
|
||||
interactive = options.get('interactive')
|
||||
|
||||
verbosity = int(options.get('verbosity', 1))
|
||||
|
||||
# Do quick and dirty validation if --noinput
|
||||
if not interactive:
|
||||
if not username or not email:
|
||||
|
@ -79,7 +80,7 @@ class Command(BaseCommand):
|
|||
# try/except to trap for a keyboard interrupt and exit gracefully.
|
||||
if interactive:
|
||||
try:
|
||||
|
||||
|
||||
# Get a username
|
||||
while 1:
|
||||
if not username:
|
||||
|
@ -100,7 +101,7 @@ class Command(BaseCommand):
|
|||
else:
|
||||
sys.stderr.write("Error: That username is already taken.\n")
|
||||
username = None
|
||||
|
||||
|
||||
# Get an email
|
||||
while 1:
|
||||
if not email:
|
||||
|
@ -112,7 +113,7 @@ class Command(BaseCommand):
|
|||
email = None
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
# Get a password
|
||||
while 1:
|
||||
if not password:
|
||||
|
@ -130,6 +131,8 @@ class Command(BaseCommand):
|
|||
except KeyboardInterrupt:
|
||||
sys.stderr.write("\nOperation cancelled.\n")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
User.objects.create_superuser(username, email, password)
|
||||
print "Superuser created successfully."
|
||||
if verbosity >= 1:
|
||||
self.stdout.write("Superuser created successfully.\n")
|
||||
|
||||
|
|
|
@ -106,7 +106,6 @@ class UserManager(models.Manager):
|
|||
"""
|
||||
Creates and saves a User with the given username, e-mail and password.
|
||||
"""
|
||||
|
||||
now = datetime.datetime.now()
|
||||
|
||||
# Normalize the address by lowercasing the domain part of the email
|
||||
|
@ -122,10 +121,7 @@ class UserManager(models.Manager):
|
|||
is_active=True, is_superuser=False, last_login=now,
|
||||
date_joined=now)
|
||||
|
||||
if password:
|
||||
user.set_password(password)
|
||||
else:
|
||||
user.set_unusable_password()
|
||||
user.set_password(password)
|
||||
user.save(using=self._db)
|
||||
return user
|
||||
|
||||
|
@ -238,11 +234,14 @@ class User(models.Model):
|
|||
return full_name.strip()
|
||||
|
||||
def set_password(self, raw_password):
|
||||
import random
|
||||
algo = 'sha1'
|
||||
salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
|
||||
hsh = get_hexdigest(algo, salt, raw_password)
|
||||
self.password = '%s$%s$%s' % (algo, salt, hsh)
|
||||
if raw_password is None:
|
||||
self.set_unusable_password()
|
||||
else:
|
||||
import random
|
||||
algo = 'sha1'
|
||||
salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
|
||||
hsh = get_hexdigest(algo, salt, raw_password)
|
||||
self.password = '%s$%s$%s' % (algo, salt, hsh)
|
||||
|
||||
def check_password(self, raw_password):
|
||||
"""
|
||||
|
@ -265,7 +264,11 @@ class User(models.Model):
|
|||
self.password = UNUSABLE_PASSWORD
|
||||
|
||||
def has_usable_password(self):
|
||||
return self.password != UNUSABLE_PASSWORD
|
||||
if self.password is None \
|
||||
or self.password == UNUSABLE_PASSWORD:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def get_group_permissions(self, obj=None):
|
||||
"""
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from django.contrib.auth.tests.auth_backends import BackendTest, RowlevelBackendTest, AnonymousUserBackendTest, NoAnonymousUserBackendTest
|
||||
from django.contrib.auth.tests.basic import BASIC_TESTS
|
||||
from django.contrib.auth.tests.basic import BasicTestCase
|
||||
from django.contrib.auth.tests.decorators import LoginRequiredTestCase
|
||||
from django.contrib.auth.tests.forms import UserCreationFormTest, AuthenticationFormTest, SetPasswordFormTest, PasswordChangeFormTest, UserChangeFormTest, PasswordResetFormTest
|
||||
from django.contrib.auth.tests.remote_user \
|
||||
|
@ -12,6 +12,5 @@ from django.contrib.auth.tests.views \
|
|||
# The password for the fixture data users is 'password'
|
||||
|
||||
__test__ = {
|
||||
'BASIC_TESTS': BASIC_TESTS,
|
||||
'TOKEN_GENERATOR_TESTS': TOKEN_GENERATOR_TESTS,
|
||||
}
|
||||
|
|
|
@ -1,77 +1,92 @@
|
|||
from django.test import TestCase
|
||||
from django.contrib.auth.models import User, AnonymousUser
|
||||
from django.core.management import call_command
|
||||
from StringIO import StringIO
|
||||
|
||||
BASIC_TESTS = """
|
||||
>>> from django.contrib.auth.models import User, AnonymousUser
|
||||
>>> u = User.objects.create_user('testuser', 'test@example.com', 'testpw')
|
||||
>>> u.has_usable_password()
|
||||
True
|
||||
>>> u.check_password('bad')
|
||||
False
|
||||
>>> u.check_password('testpw')
|
||||
True
|
||||
>>> u.set_unusable_password()
|
||||
>>> u.save()
|
||||
>>> u.check_password('testpw')
|
||||
False
|
||||
>>> u.has_usable_password()
|
||||
False
|
||||
>>> u2 = User.objects.create_user('testuser2', 'test2@example.com')
|
||||
>>> u2.has_usable_password()
|
||||
False
|
||||
class BasicTestCase(TestCase):
|
||||
def test_user(self):
|
||||
"Check that users can be created and can set their password"
|
||||
u = User.objects.create_user('testuser', 'test@example.com', 'testpw')
|
||||
self.assertTrue(u.has_usable_password())
|
||||
self.assertFalse(u.check_password('bad'))
|
||||
self.assertTrue(u.check_password('testpw'))
|
||||
|
||||
>>> u.is_authenticated()
|
||||
True
|
||||
>>> u.is_staff
|
||||
False
|
||||
>>> u.is_active
|
||||
True
|
||||
>>> u.is_superuser
|
||||
False
|
||||
# Check we can manually set an unusable password
|
||||
u.set_unusable_password()
|
||||
u.save()
|
||||
self.assertFalse(u.check_password('testpw'))
|
||||
self.assertFalse(u.has_usable_password())
|
||||
u.set_password('testpw')
|
||||
self.assertTrue(u.check_password('testpw'))
|
||||
u.set_password(None)
|
||||
self.assertFalse(u.has_usable_password())
|
||||
|
||||
>>> a = AnonymousUser()
|
||||
>>> a.is_authenticated()
|
||||
False
|
||||
>>> a.is_staff
|
||||
False
|
||||
>>> a.is_active
|
||||
False
|
||||
>>> a.is_superuser
|
||||
False
|
||||
>>> a.groups.all()
|
||||
[]
|
||||
>>> a.user_permissions.all()
|
||||
[]
|
||||
# Check authentication/permissions
|
||||
self.assertTrue(u.is_authenticated())
|
||||
self.assertFalse(u.is_staff)
|
||||
self.assertTrue(u.is_active)
|
||||
self.assertFalse(u.is_superuser)
|
||||
|
||||
# superuser tests.
|
||||
>>> super = User.objects.create_superuser('super', 'super@example.com', 'super')
|
||||
>>> super.is_superuser
|
||||
True
|
||||
>>> super.is_active
|
||||
True
|
||||
>>> super.is_staff
|
||||
True
|
||||
# Check API-based user creation with no password
|
||||
u2 = User.objects.create_user('testuser2', 'test2@example.com')
|
||||
self.assertFalse(u.has_usable_password())
|
||||
|
||||
#
|
||||
# Tests for createsuperuser management command.
|
||||
# It's nearly impossible to test the interactive mode -- a command test helper
|
||||
# would be needed (and *awesome*) -- so just test the non-interactive mode.
|
||||
# This covers most of the important validation, but not all.
|
||||
#
|
||||
>>> from django.core.management import call_command
|
||||
def test_anonymous_user(self):
|
||||
"Check the properties of the anonymous user"
|
||||
a = AnonymousUser()
|
||||
self.assertFalse(a.is_authenticated())
|
||||
self.assertFalse(a.is_staff)
|
||||
self.assertFalse(a.is_active)
|
||||
self.assertFalse(a.is_superuser)
|
||||
self.assertEqual(a.groups.all().count(), 0)
|
||||
self.assertEqual(a.user_permissions.all().count(), 0)
|
||||
|
||||
>>> call_command("createsuperuser", interactive=False, username="joe", email="joe@somewhere.org")
|
||||
Superuser created successfully.
|
||||
def test_superuser(self):
|
||||
"Check the creation and properties of a superuser"
|
||||
super = User.objects.create_superuser('super', 'super@example.com', 'super')
|
||||
self.assertTrue(super.is_superuser)
|
||||
self.assertTrue(super.is_active)
|
||||
self.assertTrue(super.is_staff)
|
||||
|
||||
>>> u = User.objects.get(username="joe")
|
||||
>>> u.email
|
||||
u'joe@somewhere.org'
|
||||
>>> u.password
|
||||
u'!'
|
||||
>>> call_command("createsuperuser", interactive=False, username="joe+admin@somewhere.org", email="joe@somewhere.org")
|
||||
Superuser created successfully.
|
||||
def test_createsuperuser_management_command(self):
|
||||
"Check the operation of the createsuperuser management command"
|
||||
# We can use the management command to create a superuser
|
||||
new_io = StringIO()
|
||||
call_command("createsuperuser",
|
||||
interactive=False,
|
||||
username="joe",
|
||||
email="joe@somewhere.org",
|
||||
stdout=new_io
|
||||
)
|
||||
command_output = new_io.getvalue().strip()
|
||||
self.assertEqual(command_output, 'Superuser created successfully.')
|
||||
u = User.objects.get(username="joe")
|
||||
self.assertEquals(u.email, 'joe@somewhere.org')
|
||||
self.assertTrue(u.check_password(''))
|
||||
|
||||
# We can supress output on the management command
|
||||
new_io = StringIO()
|
||||
call_command("createsuperuser",
|
||||
interactive=False,
|
||||
username="joe2",
|
||||
email="joe2@somewhere.org",
|
||||
verbosity=0,
|
||||
stdout=new_io
|
||||
)
|
||||
command_output = new_io.getvalue().strip()
|
||||
self.assertEqual(command_output, '')
|
||||
u = User.objects.get(username="joe2")
|
||||
self.assertEquals(u.email, 'joe2@somewhere.org')
|
||||
self.assertTrue(u.check_password(''))
|
||||
|
||||
new_io = StringIO()
|
||||
call_command("createsuperuser",
|
||||
interactive=False,
|
||||
username="joe+admin@somewhere.org",
|
||||
email="joe@somewhere.org",
|
||||
stdout=new_io
|
||||
)
|
||||
u = User.objects.get(username="joe+admin@somewhere.org")
|
||||
self.assertEquals(u.email, 'joe@somewhere.org')
|
||||
self.assertTrue(u.check_password(''))
|
||||
|
||||
>>> u = User.objects.get(username="joe+admin@somewhere.org")
|
||||
>>> u.email
|
||||
u'joe@somewhere.org'
|
||||
>>> u.password
|
||||
u'!'
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue