Author: russellm
Date: 2010-10-08 22:34:08 -0500 (Fri, 08 Oct 2010)
New Revision: 14053

Modified:
   django/trunk/AUTHORS
   django/trunk/django/contrib/auth/management/commands/createsuperuser.py
   django/trunk/django/contrib/auth/models.py
   django/trunk/django/contrib/auth/tests/__init__.py
   django/trunk/django/contrib/auth/tests/basic.py
Log:
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.

Modified: django/trunk/AUTHORS
===================================================================
--- django/trunk/AUTHORS        2010-10-09 03:23:01 UTC (rev 14052)
+++ django/trunk/AUTHORS        2010-10-09 03:34:08 UTC (rev 14053)
@@ -310,6 +310,7 @@
     Simon Litchfield <si...@quo.com.au>
     Daniel Lindsley <polarc...@gmail.com>
     Trey Long <t...@ktrl.com>
+    Laurent Luce <http://www.laurentluce.com>
     Martin Mahner <http://www.mahner.org/>
     Matt McClanahan <http://mmcc.cx/>
     Stanislaus Madueke

Modified: 
django/trunk/django/contrib/auth/management/commands/createsuperuser.py
===================================================================
--- django/trunk/django/contrib/auth/management/commands/createsuperuser.py     
2010-10-09 03:23:01 UTC (rev 14052)
+++ django/trunk/django/contrib/auth/management/commands/createsuperuser.py     
2010-10-09 03:34:08 UTC (rev 14053)
@@ -41,7 +41,8 @@
         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 @@
         # 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 @@
                     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 @@
                         email = None
                     else:
                         break
-            
+
                 # Get a password
                 while 1:
                     if not password:
@@ -130,6 +131,8 @@
             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")
+

Modified: django/trunk/django/contrib/auth/models.py
===================================================================
--- django/trunk/django/contrib/auth/models.py  2010-10-09 03:23:01 UTC (rev 
14052)
+++ django/trunk/django/contrib/auth/models.py  2010-10-09 03:34:08 UTC (rev 
14053)
@@ -106,7 +106,6 @@
         """
         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 @@
                          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 @@
         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 @@
         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):
         """

Modified: django/trunk/django/contrib/auth/tests/__init__.py
===================================================================
--- django/trunk/django/contrib/auth/tests/__init__.py  2010-10-09 03:23:01 UTC 
(rev 14052)
+++ django/trunk/django/contrib/auth/tests/__init__.py  2010-10-09 03:34:08 UTC 
(rev 14053)
@@ -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 @@
 # The password for the fixture data users is 'password'
 
 __test__ = {
-    'BASIC_TESTS': BASIC_TESTS,
     'TOKEN_GENERATOR_TESTS': TOKEN_GENERATOR_TESTS,
 }

Modified: django/trunk/django/contrib/auth/tests/basic.py
===================================================================
--- django/trunk/django/contrib/auth/tests/basic.py     2010-10-09 03:23:01 UTC 
(rev 14052)
+++ django/trunk/django/contrib/auth/tests/basic.py     2010-10-09 03:34:08 UTC 
(rev 14053)
@@ -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', 't...@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', 'te...@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', 't...@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', 'su...@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', 'te...@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="j...@somewhere.org")
-Superuser created successfully.
+    def test_superuser(self):
+        "Check the creation and properties of a superuser"
+        super = User.objects.create_superuser('super', 'su...@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'j...@somewhere.org'
->>> u.password
-u'!'
->>> call_command("createsuperuser", interactive=False, 
username="joe+ad...@somewhere.org", email="j...@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="j...@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, 'j...@somewhere.org')
+        self.assertTrue(u.check_password(''))
 
->>> u = User.objects.get(username="joe+ad...@somewhere.org")
->>> u.email
-u'j...@somewhere.org'
->>> u.password
-u'!'
-"""
+        # We can supress output on the management command
+        new_io = StringIO()
+        call_command("createsuperuser",
+            interactive=False,
+            username="joe2",
+            email="j...@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, 'j...@somewhere.org')
+        self.assertTrue(u.check_password(''))
+
+        new_io = StringIO()
+        call_command("createsuperuser",
+            interactive=False,
+            username="joe+ad...@somewhere.org",
+            email="j...@somewhere.org",
+            stdout=new_io
+        )
+        u = User.objects.get(username="joe+ad...@somewhere.org")
+        self.assertEquals(u.email, 'j...@somewhere.org')
+        self.assertTrue(u.check_password(''))
+

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to django-upda...@googlegroups.com.
To unsubscribe from this group, send email to 
django-updates+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en.

Reply via email to