Commit a3581f9a authored by Russell Keith-Magee's avatar Russell Keith-Magee
Browse files

[1.2.X] Fixed #14354 -- Normalized the handling of empty/null passwords in...

[1.2.X] 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.

Backport of r14053 from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.2.X@14054 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent 714694ae
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -307,6 +307,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
+9 −6
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ 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:
@@ -132,4 +133,6 @@ class Command(BaseCommand):
                sys.exit(1)

        User.objects.create_superuser(username, email, password)
        print "Superuser created successfully."
        if verbosity >= 1:
          self.stdout.write("Superuser created successfully.\n")
+14 −11
Original line number Diff line number Diff line
@@ -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.save(using=self._db)
        return user

@@ -238,6 +234,9 @@ class User(models.Model):
        return full_name.strip()

    def set_password(self, raw_password):
        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]
@@ -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 −2
Original line number Diff line number Diff line
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,
}
+84 −69
Original line number Diff line number Diff line
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'!'
"""