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 UserModel = get_user_model() class Command(BaseCommand): help = "Change a user's password for django.contrib.auth." requires_migrations_checks = True requires_system_checks = False def _get_pass(self, prompt="Password: "): p = getpass.getpass(prompt=prompt) if not p: raise CommandError("aborted") return p def add_arguments(self, parser): parser.add_argument( 'username', nargs='?', help='Username to change password for; by default, it\'s the current username.', ) parser.add_argument( '--database', default=DEFAULT_DB_ALIAS, help='Specifies the database to use. Default is "default".', ) def handle(self, *args, **options): if options['username']: username = options['username'] else: username = getpass.getuser() try: u = UserModel._default_manager.using(options['database']).get(**{ UserModel.USERNAME_FIELD: username }) except UserModel.DoesNotExist: raise CommandError("user '%s' does not exist" % username) self.stdout.write("Changing password for user '%s'\n" % u) MAX_TRIES = 3 count = 0 p1, p2 = 1, 2 # To make them initially mismatch. 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 += 1 # Don't validate passwords that don't match. continue try: validate_password(p2, u) except ValidationError as err: self.stderr.write('\n'.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)) u.set_password(p1) u.save() return "Password changed successfully for user '%s'" % u