Fixed #29628 -- Made createsuperuser validate password against username and required fields.
This commit is contained in:
parent
f1fbef6cd1
commit
793e9bb35a
|
@ -108,6 +108,11 @@ class Command(BaseCommand):
|
|||
if error_msg:
|
||||
raise CommandError(error_msg)
|
||||
|
||||
user_data[self.UserModel.USERNAME_FIELD] = username
|
||||
fake_user_data[self.UserModel.USERNAME_FIELD] = (
|
||||
self.username_field.remote_field.model(username) if self.username_field.remote_field
|
||||
else username
|
||||
)
|
||||
# Prompt for required fields.
|
||||
for field_name in self.UserModel.REQUIRED_FIELDS:
|
||||
if not options['interactive']:
|
||||
|
@ -134,28 +139,33 @@ class Command(BaseCommand):
|
|||
# Wrap any foreign keys in fake model instances
|
||||
if field.remote_field:
|
||||
fake_user_data[field_name] = field.remote_field.model(input_value)
|
||||
# Prompt for a password.
|
||||
while password 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.")
|
||||
if options['interactive']:
|
||||
# Prompt for a password.
|
||||
while password 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:
|
||||
validate_password(password2, self.UserModel(**fake_user_data))
|
||||
except exceptions.ValidationError as err:
|
||||
self.stderr.write('\n'.join(err.messages))
|
||||
response = input('Bypass password validation and create user anyway? [y/N]: ')
|
||||
if response.lower() != 'y':
|
||||
password = None
|
||||
# Don't validate blank passwords.
|
||||
continue
|
||||
try:
|
||||
validate_password(password2, self.UserModel(**fake_user_data))
|
||||
except exceptions.ValidationError as err:
|
||||
self.stderr.write('\n'.join(err.messages))
|
||||
response = input('Bypass password validation and create user anyway? [y/N]: ')
|
||||
if response.lower() != 'y':
|
||||
password = None
|
||||
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.")
|
||||
except KeyboardInterrupt:
|
||||
self.stderr.write('\nOperation cancelled.')
|
||||
sys.exit(1)
|
||||
|
@ -168,13 +178,6 @@ class Command(BaseCommand):
|
|||
'to create one manually.'
|
||||
)
|
||||
|
||||
if username:
|
||||
user_data[self.UserModel.USERNAME_FIELD] = username
|
||||
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.")
|
||||
|
||||
def get_input_data(self, field, message, default=None):
|
||||
"""
|
||||
Override this method if you want to customize data inputs or
|
||||
|
|
|
@ -9,7 +9,7 @@ from django.db import models
|
|||
# that every user provide a date of birth. This lets us test
|
||||
# changes in username datatype, and non-text required fields.
|
||||
class CustomUserManager(BaseUserManager):
|
||||
def create_user(self, email, date_of_birth, password=None):
|
||||
def create_user(self, email, date_of_birth, password=None, **fields):
|
||||
"""
|
||||
Creates and saves a User with the given email and password.
|
||||
"""
|
||||
|
@ -19,14 +19,15 @@ class CustomUserManager(BaseUserManager):
|
|||
user = self.model(
|
||||
email=self.normalize_email(email),
|
||||
date_of_birth=date_of_birth,
|
||||
**fields
|
||||
)
|
||||
|
||||
user.set_password(password)
|
||||
user.save(using=self._db)
|
||||
return user
|
||||
|
||||
def create_superuser(self, email, password, date_of_birth):
|
||||
u = self.create_user(email, password=password, date_of_birth=date_of_birth)
|
||||
def create_superuser(self, email, password, date_of_birth, **fields):
|
||||
u = self.create_user(email, password=password, date_of_birth=date_of_birth, **fields)
|
||||
u.is_admin = True
|
||||
u.save(using=self._db)
|
||||
return u
|
||||
|
@ -37,11 +38,12 @@ class CustomUser(AbstractBaseUser):
|
|||
is_active = models.BooleanField(default=True)
|
||||
is_admin = models.BooleanField(default=False)
|
||||
date_of_birth = models.DateField()
|
||||
first_name = models.CharField(max_length=50)
|
||||
|
||||
custom_objects = CustomUserManager()
|
||||
|
||||
USERNAME_FIELD = 'email'
|
||||
REQUIRED_FIELDS = ['date_of_birth']
|
||||
REQUIRED_FIELDS = ['date_of_birth', 'first_name']
|
||||
|
||||
def __str__(self):
|
||||
return self.email
|
||||
|
|
|
@ -29,6 +29,8 @@ MOCK_INPUT_KEY_TO_PROMPTS = {
|
|||
# @mock_inputs dict key: [expected prompt messages],
|
||||
'bypass': ['Bypass password validation and create user anyway? [y/N]: '],
|
||||
'email': ['Email address: '],
|
||||
'date_of_birth': ['Date of birth: '],
|
||||
'first_name': ['First name: '],
|
||||
'username': ['Username: ', lambda: "Username (leave blank to use '%s'): " % get_default_username()],
|
||||
}
|
||||
|
||||
|
@ -327,6 +329,7 @@ class CreatesuperuserManagementCommandTestCase(TestCase):
|
|||
interactive=False,
|
||||
email="joe@somewhere.org",
|
||||
date_of_birth="1976-04-01",
|
||||
first_name='Joe',
|
||||
stdout=new_io,
|
||||
)
|
||||
command_output = new_io.getvalue().strip()
|
||||
|
@ -552,6 +555,85 @@ class CreatesuperuserManagementCommandTestCase(TestCase):
|
|||
|
||||
test(self)
|
||||
|
||||
@override_settings(AUTH_PASSWORD_VALIDATORS=[
|
||||
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
|
||||
])
|
||||
def test_validate_password_against_username(self):
|
||||
new_io = StringIO()
|
||||
username = 'supremelycomplex'
|
||||
|
||||
def bad_then_good_password(index=[0]):
|
||||
"""Return username the first two times, then a valid password."""
|
||||
index[0] += 1
|
||||
if index[0] <= 2:
|
||||
return username
|
||||
return 'superduperunguessablepassword'
|
||||
|
||||
@mock_inputs({
|
||||
'password': bad_then_good_password,
|
||||
'username': username,
|
||||
'email': '',
|
||||
'bypass': 'n',
|
||||
})
|
||||
def test(self):
|
||||
call_command(
|
||||
'createsuperuser',
|
||||
interactive=True,
|
||||
stdin=MockTTY(),
|
||||
stdout=new_io,
|
||||
stderr=new_io,
|
||||
)
|
||||
self.assertEqual(
|
||||
new_io.getvalue().strip(),
|
||||
'The password is too similar to the username.\n'
|
||||
'Superuser created successfully.'
|
||||
)
|
||||
|
||||
test(self)
|
||||
|
||||
@override_settings(
|
||||
AUTH_USER_MODEL='auth_tests.CustomUser',
|
||||
AUTH_PASSWORD_VALIDATORS=[
|
||||
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
|
||||
]
|
||||
)
|
||||
def test_validate_password_against_required_fields(self):
|
||||
new_io = StringIO()
|
||||
username = 'josephine'
|
||||
|
||||
# Returns the username the first two times it's called, then a valid
|
||||
# password.
|
||||
def bad_then_good_password(index=[0]):
|
||||
"""Return username the first two times, then a valid password."""
|
||||
index[0] += 1
|
||||
if index[0] <= 2:
|
||||
return username
|
||||
return 'superduperunguessablepassword'
|
||||
|
||||
@mock_inputs({
|
||||
'password': bad_then_good_password,
|
||||
'username': username,
|
||||
'first_name': 'josephine',
|
||||
'date_of_birth': '1970-01-01',
|
||||
'email': 'joey@example.com',
|
||||
'bypass': 'n',
|
||||
})
|
||||
def test(self):
|
||||
call_command(
|
||||
'createsuperuser',
|
||||
interactive=True,
|
||||
stdin=MockTTY(),
|
||||
stdout=new_io,
|
||||
stderr=new_io,
|
||||
)
|
||||
self.assertEqual(
|
||||
new_io.getvalue().strip(),
|
||||
"The password is too similar to the first name.\n"
|
||||
"Superuser created successfully."
|
||||
)
|
||||
|
||||
test(self)
|
||||
|
||||
def test_blank_username(self):
|
||||
"""Creation fails if --username is blank."""
|
||||
new_io = StringIO()
|
||||
|
|
Loading…
Reference in New Issue