Search code examples
djangopermissionsdjango-admindjango-authentication

Allow non-superadmin users to reset the password for another user in django admin


I am working on a third party app running django 2.2.12.

I have an admin with 3 kinds of users: "superadmin", "staff" and "other".

When I am logged as "superadmin", when I go to the Users list and click on a user, I am able to edit them and reset their password.

However, when I try to do the same as a "staff" user, I can see the user data, but can't edit anything, and there's no option to reset the password. If I try to access the URL manually (/admin/accounts/user/[USER_ID]/password/) I get a "403 Forbidden" error message.

I noticed that if I override the has_perm method in the user model, it allows the "staff" user to edit the user data. However I would like to allow only the password change (if the user is not a superadmin or a staff user), without allowing "staff" users to edit other users.

def has_perm(self, perm, obj=None):
    return True

I have the impression this has to do with the Django permission system, but I re-read the docs at https://docs.djangoproject.com/en/2.2/topics/auth/default/ and couldn't figure this out yet.

Thanks


Solution

  • Okay, I was able to make it work with a less than ideal solution, but did the trick.

    Basically I overload the has_change_permission method of the UserAdmin class and add a custom logic to allow the password change in a special case (url ends with '/password' and user has the correct permissions.

        def can_edit_password(self, logged_user, chosen_user=None):
            if logged_user and logged_user.is_superuser:
                return True
    
            logged_user_has_change_pass_perm = (
                logged_user and
                logged_user.is_authenticated and
                logged_user.has_perm("accounts.change_student_password")
            )
            return logged_user_has_change_pass_perm
    
        def can_show_change_password_form(self, path, logged_user, chosen_user=None):
            is_changing_password = path.endswith('/password/')
            return is_changing_password and self.can_edit_password(logged_user, chosen_user)
    
        def has_change_permission(self, request, user=None):
            if self.can_show_change_password_form(path=request.path, logged_user=request.user, chosen_user=user):
                return True
            return super(UserAdmin, self).has_change_permission(request, user)