summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Schneier <josh.schneier@gmail.com>2018-08-04 18:16:47 -0400
committerTim Graham <timograham@gmail.com>2018-08-05 14:26:03 -0400
commit8b43e9b1af37ab222cbe2964ae13a9203afb9c92 (patch)
treee5797ff8eafc1b02048e41d9645fb77a4df809cf
parent89d4d412404d31ef34ae3170c0c056eff55b2a17 (diff)
Fixed #29616 -- Fixed createsuperuser for user models that don't have a password field.
-rw-r--r--django/contrib/auth/management/commands/createsuperuser.py23
-rw-r--r--tests/auth_tests/models/__init__.py3
-rw-r--r--tests/auth_tests/models/no_password.py21
-rw-r--r--tests/auth_tests/test_management.py34
4 files changed, 71 insertions, 10 deletions
diff --git a/django/contrib/auth/management/commands/createsuperuser.py b/django/contrib/auth/management/commands/createsuperuser.py
index 3a76bce90a..d700ce4c0e 100644
--- a/django/contrib/auth/management/commands/createsuperuser.py
+++ b/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,12 +63,16 @@ 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
# instances instead of raw IDs.
@@ -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.")
diff --git a/tests/auth_tests/models/__init__.py b/tests/auth_tests/models/__init__.py
index e5e38a1e2e..3422255d33 100644
--- a/tests/auth_tests/models/__init__.py
+++ b/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',
)
diff --git a/tests/auth_tests/models/no_password.py b/tests/auth_tests/models/no_password.py
new file mode 100644
index 0000000000..c7c40f76f6
--- /dev/null
+++ b/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()
diff --git a/tests/auth_tests/test_management.py b/tests/auth_tests/test_management.py
index 0c9ad81f3f..1d340c56f0 100644
--- a/tests/auth_tests/test_management.py
+++ b/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