2
0
Эх сурвалжийг харах

Fixed #29616 -- Fixed createsuperuser for user models that don't have a password field.

Josh Schneier 6 жил өмнө
parent
commit
8b43e9b1af

+ 14 - 9
django/contrib/auth/management/commands/createsuperuser.py

@@ -17,6 +17,9 @@ class NotRunningInTTYException(Exception):
     pass
 
 
+PASSWORD_FIELD = 'password'
+
+
 class Command(BaseCommand):
     help = 'Used to create a superuser.'
     requires_migrations_checks = True
@@ -60,11 +63,15 @@ class Command(BaseCommand):
     def handle(self, *args, **options):
         username = options[self.UserModel.USERNAME_FIELD]
         database = options['database']
-
-        # If not provided, create the user with an unusable password
-        password = None
         user_data = {}
         verbose_field_name = self.username_field.verbose_name
+        try:
+            self.UserModel._meta.get_field(PASSWORD_FIELD)
+        except exceptions.FieldDoesNotExist:
+            pass
+        else:
+            # If not provided, create the user with an unusable password.
+            user_data[PASSWORD_FIELD] = None
         try:
             if options['interactive']:
                 # Same as user_data but with foreign keys as fake model
@@ -109,18 +116,16 @@ class Command(BaseCommand):
                         if field.remote_field:
                             fake_user_data[field_name] = field.remote_field.model(input_value)
 
-                # Prompt for a password.
-                while password is None:
+                # Prompt for a password if the model has one.
+                while PASSWORD_FIELD in user_data and user_data[PASSWORD_FIELD] is None:
                     password = getpass.getpass()
                     password2 = getpass.getpass('Password (again): ')
                     if password != password2:
                         self.stderr.write("Error: Your passwords didn't match.")
-                        password = None
                         # Don't validate passwords that don't match.
                         continue
                     if password.strip() == '':
                         self.stderr.write("Error: Blank passwords aren't allowed.")
-                        password = None
                         # Don't validate blank passwords.
                         continue
                     try:
@@ -129,7 +134,8 @@ class Command(BaseCommand):
                         self.stderr.write('\n'.join(err.messages))
                         response = input('Bypass password validation and create user anyway? [y/N]: ')
                         if response.lower() != 'y':
-                            password = None
+                            continue
+                    user_data[PASSWORD_FIELD] = password
             else:
                 # Non-interactive mode.
                 if username is None:
@@ -147,7 +153,6 @@ class Command(BaseCommand):
                     else:
                         raise CommandError('You must use --%s with --noinput.' % field_name)
 
-            user_data['password'] = password
             self.UserModel._default_manager.db_manager(database).create_superuser(**user_data)
             if options['verbosity'] >= 1:
                 self.stdout.write("Superuser created successfully.")

+ 2 - 1
tests/auth_tests/models/__init__.py

@@ -5,6 +5,7 @@ from .custom_user import (
 from .invalid_models import CustomUserNonUniqueUsername
 from .is_active import IsActiveTestUser1
 from .minimal import MinimalUser
+from .no_password import NoPasswordUser
 from .uuid_pk import UUIDUser
 from .with_foreign_key import CustomUserWithFK, Email
 from .with_integer_username import IntegerUsernameUser
@@ -13,6 +14,6 @@ from .with_last_login_attr import UserWithDisabledLastLoginField
 __all__ = (
     'CustomUser', 'CustomUserWithoutIsActiveField', 'CustomPermissionsUser',
     'CustomUserWithFK', 'Email', 'ExtensionUser', 'IsActiveTestUser1',
-    'MinimalUser', 'UUIDUser', 'CustomUserNonUniqueUsername',
+    'MinimalUser', 'NoPasswordUser', 'UUIDUser', 'CustomUserNonUniqueUsername',
     'IntegerUsernameUser', 'UserWithDisabledLastLoginField',
 )

+ 21 - 0
tests/auth_tests/models/no_password.py

@@ -0,0 +1,21 @@
+from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
+from django.db import models
+
+
+class UserManager(BaseUserManager):
+    def _create_user(self, username, **extra_fields):
+        user = self.model(username=username, **extra_fields)
+        user.save(using=self._db)
+        return user
+
+    def create_superuser(self, username=None, **extra_fields):
+        return self._create_user(username, **extra_fields)
+
+
+class NoPasswordUser(AbstractBaseUser):
+    password = None
+    last_login = None
+    username = models.CharField(max_length=50, unique=True)
+
+    USERNAME_FIELD = 'username'
+    objects = UserManager()

+ 34 - 0
tests/auth_tests/test_management.py

@@ -879,6 +879,40 @@ class CreatesuperuserManagementCommandTestCase(TestCase):
 
         test(self)
 
+    @override_settings(AUTH_USER_MODEL='auth_tests.NoPasswordUser')
+    def test_usermodel_without_password(self):
+        new_io = StringIO()
+
+        def test(self):
+            call_command(
+                'createsuperuser',
+                interactive=False,
+                stdin=MockTTY(),
+                stdout=new_io,
+                stderr=new_io,
+                username='username',
+            )
+            self.assertEqual(new_io.getvalue().strip(), 'Superuser created successfully.')
+
+        test(self)
+
+    @override_settings(AUTH_USER_MODEL='auth_tests.NoPasswordUser')
+    def test_usermodel_without_password_interactive(self):
+        new_io = StringIO()
+
+        @mock_inputs({'username': 'username'})
+        def test(self):
+            call_command(
+                'createsuperuser',
+                interactive=True,
+                stdin=MockTTY(),
+                stdout=new_io,
+                stderr=new_io,
+            )
+            self.assertEqual(new_io.getvalue().strip(), 'Superuser created successfully.')
+
+        test(self)
+
 
 class MultiDBCreatesuperuserTestCase(TestCase):
     multi_db = True