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>
|
Simon Litchfield <simon@quo.com.au>
|
||||||
Daniel Lindsley <polarcowz@gmail.com>
|
Daniel Lindsley <polarcowz@gmail.com>
|
||||||
Trey Long <trey@ktrl.com>
|
Trey Long <trey@ktrl.com>
|
||||||
|
Laurent Luce <http://www.laurentluce.com>
|
||||||
Martin Mahner <http://www.mahner.org/>
|
Martin Mahner <http://www.mahner.org/>
|
||||||
Matt McClanahan <http://mmcc.cx/>
|
Matt McClanahan <http://mmcc.cx/>
|
||||||
Stanislaus Madueke
|
Stanislaus Madueke
|
||||||
|
|
|
@ -41,6 +41,7 @@ class Command(BaseCommand):
|
||||||
username = options.get('username', None)
|
username = options.get('username', None)
|
||||||
email = options.get('email', None)
|
email = options.get('email', None)
|
||||||
interactive = options.get('interactive')
|
interactive = options.get('interactive')
|
||||||
|
verbosity = int(options.get('verbosity', 1))
|
||||||
|
|
||||||
# Do quick and dirty validation if --noinput
|
# Do quick and dirty validation if --noinput
|
||||||
if not interactive:
|
if not interactive:
|
||||||
|
@ -132,4 +133,6 @@ class Command(BaseCommand):
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
User.objects.create_superuser(username, email, password)
|
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.
|
Creates and saves a User with the given username, e-mail and password.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
|
|
||||||
# Normalize the address by lowercasing the domain part of the email
|
# 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,
|
is_active=True, is_superuser=False, last_login=now,
|
||||||
date_joined=now)
|
date_joined=now)
|
||||||
|
|
||||||
if password:
|
user.set_password(password)
|
||||||
user.set_password(password)
|
|
||||||
else:
|
|
||||||
user.set_unusable_password()
|
|
||||||
user.save(using=self._db)
|
user.save(using=self._db)
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
@ -238,11 +234,14 @@ class User(models.Model):
|
||||||
return full_name.strip()
|
return full_name.strip()
|
||||||
|
|
||||||
def set_password(self, raw_password):
|
def set_password(self, raw_password):
|
||||||
import random
|
if raw_password is None:
|
||||||
algo = 'sha1'
|
self.set_unusable_password()
|
||||||
salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
|
else:
|
||||||
hsh = get_hexdigest(algo, salt, raw_password)
|
import random
|
||||||
self.password = '%s$%s$%s' % (algo, salt, hsh)
|
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):
|
def check_password(self, raw_password):
|
||||||
"""
|
"""
|
||||||
|
@ -265,7 +264,11 @@ class User(models.Model):
|
||||||
self.password = UNUSABLE_PASSWORD
|
self.password = UNUSABLE_PASSWORD
|
||||||
|
|
||||||
def has_usable_password(self):
|
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):
|
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.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.decorators import LoginRequiredTestCase
|
||||||
from django.contrib.auth.tests.forms import UserCreationFormTest, AuthenticationFormTest, SetPasswordFormTest, PasswordChangeFormTest, UserChangeFormTest, PasswordResetFormTest
|
from django.contrib.auth.tests.forms import UserCreationFormTest, AuthenticationFormTest, SetPasswordFormTest, PasswordChangeFormTest, UserChangeFormTest, PasswordResetFormTest
|
||||||
from django.contrib.auth.tests.remote_user \
|
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'
|
# The password for the fixture data users is 'password'
|
||||||
|
|
||||||
__test__ = {
|
__test__ = {
|
||||||
'BASIC_TESTS': BASIC_TESTS,
|
|
||||||
'TOKEN_GENERATOR_TESTS': TOKEN_GENERATOR_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 = """
|
class BasicTestCase(TestCase):
|
||||||
>>> from django.contrib.auth.models import User, AnonymousUser
|
def test_user(self):
|
||||||
>>> u = User.objects.create_user('testuser', 'test@example.com', 'testpw')
|
"Check that users can be created and can set their password"
|
||||||
>>> u.has_usable_password()
|
u = User.objects.create_user('testuser', 'test@example.com', 'testpw')
|
||||||
True
|
self.assertTrue(u.has_usable_password())
|
||||||
>>> u.check_password('bad')
|
self.assertFalse(u.check_password('bad'))
|
||||||
False
|
self.assertTrue(u.check_password('testpw'))
|
||||||
>>> 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
|
|
||||||
|
|
||||||
>>> u.is_authenticated()
|
# Check we can manually set an unusable password
|
||||||
True
|
u.set_unusable_password()
|
||||||
>>> u.is_staff
|
u.save()
|
||||||
False
|
self.assertFalse(u.check_password('testpw'))
|
||||||
>>> u.is_active
|
self.assertFalse(u.has_usable_password())
|
||||||
True
|
u.set_password('testpw')
|
||||||
>>> u.is_superuser
|
self.assertTrue(u.check_password('testpw'))
|
||||||
False
|
u.set_password(None)
|
||||||
|
self.assertFalse(u.has_usable_password())
|
||||||
|
|
||||||
>>> a = AnonymousUser()
|
# Check authentication/permissions
|
||||||
>>> a.is_authenticated()
|
self.assertTrue(u.is_authenticated())
|
||||||
False
|
self.assertFalse(u.is_staff)
|
||||||
>>> a.is_staff
|
self.assertTrue(u.is_active)
|
||||||
False
|
self.assertFalse(u.is_superuser)
|
||||||
>>> a.is_active
|
|
||||||
False
|
|
||||||
>>> a.is_superuser
|
|
||||||
False
|
|
||||||
>>> a.groups.all()
|
|
||||||
[]
|
|
||||||
>>> a.user_permissions.all()
|
|
||||||
[]
|
|
||||||
|
|
||||||
# superuser tests.
|
# Check API-based user creation with no password
|
||||||
>>> super = User.objects.create_superuser('super', 'super@example.com', 'super')
|
u2 = User.objects.create_user('testuser2', 'test2@example.com')
|
||||||
>>> super.is_superuser
|
self.assertFalse(u.has_usable_password())
|
||||||
True
|
|
||||||
>>> super.is_active
|
|
||||||
True
|
|
||||||
>>> super.is_staff
|
|
||||||
True
|
|
||||||
|
|
||||||
#
|
def test_anonymous_user(self):
|
||||||
# Tests for createsuperuser management command.
|
"Check the properties of the anonymous user"
|
||||||
# It's nearly impossible to test the interactive mode -- a command test helper
|
a = AnonymousUser()
|
||||||
# would be needed (and *awesome*) -- so just test the non-interactive mode.
|
self.assertFalse(a.is_authenticated())
|
||||||
# This covers most of the important validation, but not all.
|
self.assertFalse(a.is_staff)
|
||||||
#
|
self.assertFalse(a.is_active)
|
||||||
>>> from django.core.management import call_command
|
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")
|
def test_superuser(self):
|
||||||
Superuser created successfully.
|
"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")
|
def test_createsuperuser_management_command(self):
|
||||||
>>> u.email
|
"Check the operation of the createsuperuser management command"
|
||||||
u'joe@somewhere.org'
|
# We can use the management command to create a superuser
|
||||||
>>> u.password
|
new_io = StringIO()
|
||||||
u'!'
|
call_command("createsuperuser",
|
||||||
>>> call_command("createsuperuser", interactive=False, username="joe+admin@somewhere.org", email="joe@somewhere.org")
|
interactive=False,
|
||||||
Superuser created successfully.
|
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