Loading django/contrib/auth/management/commands/changepassword.py +14 −2 Original line number Diff line number Diff line Loading @@ -3,6 +3,8 @@ from __future__ import unicode_literals import getpass from django.contrib.auth import get_user_model from django.contrib.auth.password_validation import validate_password from django.core.exceptions import ValidationError from django.core.management.base import BaseCommand, CommandError from django.db import DEFAULT_DB_ALIAS from django.utils.encoding import force_str Loading Loading @@ -46,12 +48,22 @@ class Command(BaseCommand): MAX_TRIES = 3 count = 0 p1, p2 = 1, 2 # To make them initially mismatch. while p1 != p2 and count < MAX_TRIES: password_validated = False while (p1 != p2 or not password_validated) and count < MAX_TRIES: p1 = self._get_pass() p2 = self._get_pass("Password (again): ") if p1 != p2: self.stdout.write("Passwords do not match. Please try again.\n") count = count + 1 count += 1 # Don't validate passwords that don't match. continue try: validate_password(p2, u) except ValidationError as err: self.stdout.write(', '.join(err.messages)) count += 1 else: password_validated = True if count == MAX_TRIES: raise CommandError("Aborting password change for user '%s' after %s attempts" % (u, count)) Loading django/contrib/auth/management/commands/createsuperuser.py +19 −1 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ import sys from django.contrib.auth import get_user_model from django.contrib.auth.management import get_default_username from django.contrib.auth.password_validation import validate_password from django.core import exceptions from django.core.management.base import BaseCommand, CommandError from django.db import DEFAULT_DB_ALIAS Loading Loading @@ -56,6 +57,9 @@ class Command(BaseCommand): # If not provided, create the user with an unusable password password = None user_data = {} # Same as user_data but with foreign keys as fake model instances # instead of raw IDs. fake_user_data = {} # Do quick and dirty validation if --noinput if not options['interactive']: Loading Loading @@ -121,7 +125,13 @@ class Command(BaseCommand): field.remote_field.field_name, ) if field.remote_field else '', )) user_data[field_name] = self.get_input_data(field, message) input_value = self.get_input_data(field, message) user_data[field_name] = input_value fake_user_data[field_name] = input_value # Wrap any foreign keys in fake model instances if field.remote_field: fake_user_data[field_name] = field.remote_field.model(input_value) # Get a password while password is None: Loading @@ -130,13 +140,21 @@ class Command(BaseCommand): 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(', '.join(err.messages)) password = None except KeyboardInterrupt: self.stderr.write("\nOperation cancelled.") sys.exit(1) Loading tests/auth_tests/test_management.py +56 −2 Original line number Diff line number Diff line Loading @@ -43,6 +43,8 @@ def mock_inputs(inputs): if six.PY2: # getpass on Windows only supports prompt as bytestring (#19807) assert isinstance(prompt, six.binary_type) if callable(inputs['password']): return inputs['password']() return inputs['password'] def mock_input(prompt): Loading Loading @@ -107,6 +109,9 @@ class GetDefaultUsernameTestCase(TestCase): self.assertEqual(management.get_default_username(), 'julia') @override_settings(AUTH_PASSWORD_VALIDATORS=[ {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'}, ]) class ChangepasswordManagementCommandTestCase(TestCase): def setUp(self): Loading Loading @@ -139,11 +144,24 @@ class ChangepasswordManagementCommandTestCase(TestCase): mismatched passwords three times. """ command = changepassword.Command() command._get_pass = lambda *args: args or 'foo' command._get_pass = lambda *args: str(args) or 'foo' with self.assertRaises(CommandError): command.execute(username="joe", stdout=self.stdout, stderr=self.stderr) def test_password_validation(self): """ A CommandError should be raised if the user enters in passwords which fail validation three times. """ command = changepassword.Command() command._get_pass = lambda *args: '1234567890' abort_msg = "Aborting password change for user 'joe' after 3 attempts" with self.assertRaisesMessage(CommandError, abort_msg): command.execute(username="joe", stdout=self.stdout, stderr=self.stderr) self.assertIn('This password is entirely numeric.', self.stdout.getvalue()) def test_that_changepassword_command_works_with_nonascii_output(self): """ #21627 -- Executing the changepassword management command should allow Loading @@ -158,7 +176,10 @@ class ChangepasswordManagementCommandTestCase(TestCase): command.execute(username="J\xfalia", stdout=self.stdout) @override_settings(SILENCED_SYSTEM_CHECKS=['fields.W342']) # ForeignKey(unique=True) @override_settings( SILENCED_SYSTEM_CHECKS=['fields.W342'], # ForeignKey(unique=True) AUTH_PASSWORD_VALIDATORS=[{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'}], ) class CreatesuperuserManagementCommandTestCase(TestCase): def test_basic_usage(self): Loading Loading @@ -443,6 +464,39 @@ class CreatesuperuserManagementCommandTestCase(TestCase): test(self) def test_password_validation(self): """ Creation should fail if the password fails validation. """ new_io = six.StringIO() # Returns '1234567890' the first two times it is called, then # 'password' subsequently. def bad_then_good_password(index=[0]): index[0] += 1 if index[0] <= 2: return '1234567890' return 'password' @mock_inputs({ 'password': bad_then_good_password, 'username': 'joe1234567890', }) def test(self): call_command( "createsuperuser", interactive=True, stdin=MockTTY(), stdout=new_io, stderr=new_io, ) self.assertEqual( new_io.getvalue().strip(), "This password is entirely numeric.\n" "Superuser created successfully." ) test(self) class CustomUserModelValidationTestCase(SimpleTestCase): @override_settings(AUTH_USER_MODEL='auth.CustomUserNonListRequiredFields') Loading Loading
django/contrib/auth/management/commands/changepassword.py +14 −2 Original line number Diff line number Diff line Loading @@ -3,6 +3,8 @@ from __future__ import unicode_literals import getpass from django.contrib.auth import get_user_model from django.contrib.auth.password_validation import validate_password from django.core.exceptions import ValidationError from django.core.management.base import BaseCommand, CommandError from django.db import DEFAULT_DB_ALIAS from django.utils.encoding import force_str Loading Loading @@ -46,12 +48,22 @@ class Command(BaseCommand): MAX_TRIES = 3 count = 0 p1, p2 = 1, 2 # To make them initially mismatch. while p1 != p2 and count < MAX_TRIES: password_validated = False while (p1 != p2 or not password_validated) and count < MAX_TRIES: p1 = self._get_pass() p2 = self._get_pass("Password (again): ") if p1 != p2: self.stdout.write("Passwords do not match. Please try again.\n") count = count + 1 count += 1 # Don't validate passwords that don't match. continue try: validate_password(p2, u) except ValidationError as err: self.stdout.write(', '.join(err.messages)) count += 1 else: password_validated = True if count == MAX_TRIES: raise CommandError("Aborting password change for user '%s' after %s attempts" % (u, count)) Loading
django/contrib/auth/management/commands/createsuperuser.py +19 −1 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ import sys from django.contrib.auth import get_user_model from django.contrib.auth.management import get_default_username from django.contrib.auth.password_validation import validate_password from django.core import exceptions from django.core.management.base import BaseCommand, CommandError from django.db import DEFAULT_DB_ALIAS Loading Loading @@ -56,6 +57,9 @@ class Command(BaseCommand): # If not provided, create the user with an unusable password password = None user_data = {} # Same as user_data but with foreign keys as fake model instances # instead of raw IDs. fake_user_data = {} # Do quick and dirty validation if --noinput if not options['interactive']: Loading Loading @@ -121,7 +125,13 @@ class Command(BaseCommand): field.remote_field.field_name, ) if field.remote_field else '', )) user_data[field_name] = self.get_input_data(field, message) input_value = self.get_input_data(field, message) user_data[field_name] = input_value fake_user_data[field_name] = input_value # Wrap any foreign keys in fake model instances if field.remote_field: fake_user_data[field_name] = field.remote_field.model(input_value) # Get a password while password is None: Loading @@ -130,13 +140,21 @@ class Command(BaseCommand): 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(', '.join(err.messages)) password = None except KeyboardInterrupt: self.stderr.write("\nOperation cancelled.") sys.exit(1) Loading
tests/auth_tests/test_management.py +56 −2 Original line number Diff line number Diff line Loading @@ -43,6 +43,8 @@ def mock_inputs(inputs): if six.PY2: # getpass on Windows only supports prompt as bytestring (#19807) assert isinstance(prompt, six.binary_type) if callable(inputs['password']): return inputs['password']() return inputs['password'] def mock_input(prompt): Loading Loading @@ -107,6 +109,9 @@ class GetDefaultUsernameTestCase(TestCase): self.assertEqual(management.get_default_username(), 'julia') @override_settings(AUTH_PASSWORD_VALIDATORS=[ {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'}, ]) class ChangepasswordManagementCommandTestCase(TestCase): def setUp(self): Loading Loading @@ -139,11 +144,24 @@ class ChangepasswordManagementCommandTestCase(TestCase): mismatched passwords three times. """ command = changepassword.Command() command._get_pass = lambda *args: args or 'foo' command._get_pass = lambda *args: str(args) or 'foo' with self.assertRaises(CommandError): command.execute(username="joe", stdout=self.stdout, stderr=self.stderr) def test_password_validation(self): """ A CommandError should be raised if the user enters in passwords which fail validation three times. """ command = changepassword.Command() command._get_pass = lambda *args: '1234567890' abort_msg = "Aborting password change for user 'joe' after 3 attempts" with self.assertRaisesMessage(CommandError, abort_msg): command.execute(username="joe", stdout=self.stdout, stderr=self.stderr) self.assertIn('This password is entirely numeric.', self.stdout.getvalue()) def test_that_changepassword_command_works_with_nonascii_output(self): """ #21627 -- Executing the changepassword management command should allow Loading @@ -158,7 +176,10 @@ class ChangepasswordManagementCommandTestCase(TestCase): command.execute(username="J\xfalia", stdout=self.stdout) @override_settings(SILENCED_SYSTEM_CHECKS=['fields.W342']) # ForeignKey(unique=True) @override_settings( SILENCED_SYSTEM_CHECKS=['fields.W342'], # ForeignKey(unique=True) AUTH_PASSWORD_VALIDATORS=[{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'}], ) class CreatesuperuserManagementCommandTestCase(TestCase): def test_basic_usage(self): Loading Loading @@ -443,6 +464,39 @@ class CreatesuperuserManagementCommandTestCase(TestCase): test(self) def test_password_validation(self): """ Creation should fail if the password fails validation. """ new_io = six.StringIO() # Returns '1234567890' the first two times it is called, then # 'password' subsequently. def bad_then_good_password(index=[0]): index[0] += 1 if index[0] <= 2: return '1234567890' return 'password' @mock_inputs({ 'password': bad_then_good_password, 'username': 'joe1234567890', }) def test(self): call_command( "createsuperuser", interactive=True, stdin=MockTTY(), stdout=new_io, stderr=new_io, ) self.assertEqual( new_io.getvalue().strip(), "This password is entirely numeric.\n" "Superuser created successfully." ) test(self) class CustomUserModelValidationTestCase(SimpleTestCase): @override_settings(AUTH_USER_MODEL='auth.CustomUserNonListRequiredFields') Loading