Search code examples
djangohashpasswords

Hashing the password if it is not hashed in django


When I try to create a password for the user using the admin interface is doesn't get hashed. So I added this line in the user model

    def save(self, *args, **kwargs):
        self.set_password(self.password)

While this solves the issue. while creating user using createsuperuser it saves the password as a hash But when I try to login into the admin interface is says incorrect password. I'm sure it's caused by the adding the above line into the code. When i remove the line, createsuperuser works fine but password isn't hashed in the admin interface, when i add the line admin works while createsuperuser doesn't. I want to hash the password if it isn't hashed yet.

class CustomUser(AbstractUser):
    role = models.ForeignKey(to=Role, to_field='role', on_delete=models.CASCADE, null=True, blank=True)
    username = models.CharField(max_length=100, unique=True, editable=True, validators=[MinLengthValidator(5)], null=False)
    image = models.ImageField(upload_to='profile_pics/', blank=True)
    password = models.CharField(max_length=128)
    complaint = GenericRelation(Complaint)
    # Specify unique related names for the groups and user_permissions fields
    groups = models.ManyToManyField(
        Group,
        verbose_name=_('groups'),
        blank=True,
        help_text=_(
            'The groups this user belongs to. A user will get all permissions '
            'granted to each of their groups.'
        ),
        related_name='custom_users',  # Change the related name to 'custom_users'
    )

    user_permissions = models.ManyToManyField(
        Permission,
        verbose_name=_('user permissions'),
        blank=True,
        help_text=_('Specific permissions for this user.'),
        related_name='custom_users',  # Change the related name to 'custom_users'
    )

    def __str__(self):
        return f'{self.username}'

    def save(self, *args, **kwargs):
        if self.role_id == 'admin':
            self.is_staff = True

        if self.image:
            img = Image.open(self.image.path)

            if img.height > 300 or img.width > 300:
                output_size = (300, 300)
                img.thumbnail(output_size)
                img.save(self.image.path)
        super().save(*args, **kwargs)
@admin.register(CustomUser)
class CustomUserAdmin(admin.ModelAdmin):
    list_display = ('username', 'password', 'image', 'role')


Solution

  • Don't hash in the .save(…) method: this will hash the password each time you save the model object again. Since createsuperuser already hashes the password, it would thus hash the password a second time, making it useless for the (first) password. It would require logging in with the hash of the first password.

    The way Django's default admin for User works is by plugging in a form that hashes it. We can plug this in for the custom admin as well:

    from django.contrib.auth.forms import UserChangeForm, UserCreationForm
    
    
    class CustomUserCreationForm(UserCreationForm):
        class Meta:
            model = CustomUser
            fields = ('username', 'role', 'image')
    
    
    class CustomUserChangeForm(UserChangeForm):
        class Meta:
            model = CustomUser
            fields = ('username', 'role', 'image')
    
    
    @admin.register(CustomUser)
    class CustomUserAdmin(admin.ModelAdmin):
        list_display = ('username', 'password', 'image', 'role')
        form = CustomUserChangeForm
        add_form = CustomUserCreationForm

    This will also ensure that the password is shown as an <input type="password'> so hiding the password on the screen.