Search code examples
pythondjangodjango-modelsdjango-admin

Password field is visible and not encrypted in Django admin site


So to use email as username I override the build-in User model like this (inspired by Django source code)

models.py

class User(AbstractUser):
    username = None
    email = models.EmailField(unique=True)
    objects = UserManager()
    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = []

    def __str__(self):
        return self.email

admin.py

@admin.register(User)
class UserAdmin(admin.ModelAdmin):
    fieldsets = (
        (None, {"fields": ("email", "password")}),
        (("Personal info"), {"fields": ("first_name", "last_name")}),
        (
            ("Permissions"),
            {
                "fields": (
                    "is_active",
                    "is_staff",
                    "is_superuser",
                    "groups",
                    "user_permissions",
                ),
            },
        ),
        (("Important dates"), {"fields": ("last_login", "date_joined")}),
    )
    add_fieldsets = (
        (
            None,
            {
                "classes": ("wide",),
                "fields": ("email", "password1", "password2"),
            },
        ),
    )
    list_display = ("email", "is_active", "is_staff", "is_superuser")
    list_filter = ("is_active", "is_staff", "is_superuser")
    search_fields = ("email",)
    ordering = ("email",)
    filter_horizontal = ("groups", "user_permissions",)

But this is how it looks like when I go to Admin site to change a user:

enter image description here

Password is visible and not hashed and no link to change password form.

Comparing to what it looks like on a default Django project:

enter image description here

Password is not visible and there's a link to change password form

So clearly I'm missing something but I can't figure out what it is.


Solution

  • You are not able to see the password in hashed state because the password field is a CharField which renders it as normal text field. In Django's admin side there's a field called ReadOnlyPasswordHashField in django.contrib.auth.forms which renders the password field to be in hashed state with password change link.

    Django's UserAdmin uses different form classes for user creation and updation.

        form = UserChangeForm
        add_form = UserCreationForm
        change_password_form = AdminPasswordChangeForm
    

    To edit user details UserAdmin uses form = UserChangeForm(source code) where the password field is set as ReadOnlyPasswordHashField(source code).

    class UserChangeForm(forms.ModelForm):
        password = ReadOnlyPasswordHashField(
            label=_("Password"),
            help_text=_(
                "Raw passwords are not stored, so there is no way to see this "
                "user’s password, but you can change the password using "
                '<a href="{}">this form</a>.'
            ),
        )
    

    So, Just by inheriting from UserAdmin from django.contrib.auth.admin would make the password to be in hashed state with all the other essentials as seen in default admin site for users.

    OR

    you could simply import UserChangeForm from django.contrib.auth.forms and set form = UserChangeForm in custom UserAdmin

    from django.contrib.auth.forms import UserChangeForm,AdminPasswordChangeForm
    
    # code
    
    @admin.register(User)
    class UserAdmin(admin.ModelAdmin):
        # code
        form = UserChangeForm
        change_password_form = AdminPasswordChangeForm
        # code