Search code examples
djangodjango-admindjango-authenticationdjango-usersopenwisp

How to create a user who can create another user but not grant permissions(only grant predefined group permissions) in django?


I want to be able to create a user (in openwisp2 which is django based), who in turn, can create another user but not be able to grant individual permissions for this user. Only only granting predefined group permissions should be allowed for this new user.

When I gave user add permission to a user, I saw that this user gets the 'permission add' option by default(eventhough I have not granted the 'permission adding' permission for this user). I observed that this new user had the privilege to create a superuser aswell(which was quite surprising)


Solution

  • Unfortunately it is not possible to achieve what you want out of the box with OpenWISP 2 because of how the default django user & permission system works.

    Once a user has the permission to add and change details of users, he will also be able to add / remove the superuser flag on other user. Therefore that permission should be only granted to trusted users.

    In order to achieve what you want, you need to change the UserAdmin class of the openwisp-users module.

    I have tried these changes which seem to work pretty well:

    class UserAdmin(BaseUserAdmin, BaseAdmin):
        # ... omitting existing code for brevity ...
    
        def get_readonly_fields(self, request, obj=None):
            # retrieve readonly fields
            fields = super(UserAdmin, self).get_readonly_fields(request, obj)
            # do not allow operators to set the is_superuser flag
            if not request.user.is_superuser:
                fields += fields[:] + ['is_superuser']  # copy to avoid modifying reference
            return fields
    
        def has_change_permission(self, request, obj=None):
            # do not allow operators to edit details of superusers
            # returns 403 if trying to access the change form of a superuser
            if obj and obj.is_superuser and not request.user.is_superuser:
                return False
            return super(UserAdmin, self).has_change_permission(request, obj)
    
        def get_queryset(self, request):
            qs = super(UserAdmin, self).get_queryset(request)
            # hide superusers from operators (they can't edit their details)
            if not request.user.is_superuser:
                qs = qs.filter(is_superuser=False)
            return qs
    

    These changes implement the following 3 things:

    • make the is_superuser field readonly for non superusers (aka operators)
    • hide superusers to non superusers in the user list
    • explicitly forbid changing details of superusers to non superusers (aka operators)

    These changes could be integrated in openwisp2 as well. Enjoy and try to contribute if you can (eg: open an issue or pull request in openwisp-users)!

    PS: I've included this feature (plus tests and improvements) in the openwisp-users module.