在授予"用户更改"权限时,如何防止Django管理员中的权限升级?

Dav*_*Eyk 21 django permissions django-admin django-users

我有一个拥有大量客户群的django网站.我想让我们的客户服务部门能够改变正常的用户帐户,做更改密码,电子邮件地址等等.但是,如果我授予某人内置auth | user | Can change user权限,他们就可以is_superuser在任何人身上设置标志.帐户,包括他们自己.(!!!)

为非超级用户员工删除此选项的最佳方法是什么?我确定它涉及子类化django.contrib.auth.forms.UserChangeForm并将其挂钩到我已经自定义的UserAdmin对象......不知何故.但我找不到任何关于如何做到这一点的文档,我还不太了解内部情况.

Clé*_*ent 21

他们可以在任何帐户上设置is_superuser标志,包括他们自己的标志.(!!!)

不仅如此,他们还能够逐个给予自己任何权限,同样的效果......

我确定它涉及子类化django.contrib.auth.forms.UserChangeForm

嗯,不一定.您在django管理员的更改页面中看到的表单是由管理应用程序动态创建的,并且基于UserChangeForm此类,但此类几乎不会向该username字段添加正则表达式验证.

并将其挂钩到我已经定制的UserAdmin对象中......

定制UserAdmin是去这里的方式.基本上,您想要将fieldsets属性更改为:

class MyUserAdmin(UserAdmin):
    fieldsets = (
        (None, {'fields': ('username', 'password')}),
        (_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
        # Removing the permission part
        # (_('Permissions'), {'fields': ('is_staff', 'is_active', 'is_superuser', 'user_permissions')}),
        (_('Important dates'), {'fields': ('last_login', 'date_joined')}),
        # Keeping the group parts? Ok, but they shouldn't be able to define
        # their own groups, up to you...
        (_('Groups'), {'fields': ('groups',)}),
    )
Run Code Online (Sandbox Code Playgroud)

但问题是这个限制将适用于所有用户.如果这不是您想要的,您可以例如change_view根据用户的权限覆盖以表现不同.代码段:

class MyUserAdmin(UserAdmin):
    staff_fieldsets = (
        (None, {'fields': ('username', 'password')}),
        (_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
        # No permissions
        (_('Important dates'), {'fields': ('last_login', 'date_joined')}),
        (_('Groups'), {'fields': ('groups',)}),
    )

    def change_view(self, request, *args, **kwargs):
        # for non-superuser
        if not request.user.is_superuser:
            try:
                self.fieldsets = self.staff_fieldsets
                response = super(MyUserAdmin, self).change_view(request, *args, **kwargs)
            finally:
                # Reset fieldsets to its original value
                self.fieldsets = UserAdmin.fieldsets
            return response
        else:
            return super(MyUserAdmin, self).change_view(request, *args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

  • 非超级用户仍然可以更改超级用户的密码并使用她的帐户登录.为了禁止这一点,我从`staff_fieldsets`中删除了密码字段,写了一个围绕`UserAdmin.user_change_password`的包装器,它不允许为非超级用户更改超级用户的密码,最后添加了一个指向``password /"的链接.在第一个fieldsets'`description`中. (3认同)
  • 隐藏田地是不够的.具有足够的Web开发知识的用户将能够使用wget在适当的URL上创建自己的POST请求. (2认同)

dkm*_*ita 6

接受的答案的以下部分具有竞争条件,如果两个工作人员用户同时尝试访问管理表单,则其中一个可以获得超级用户表单.

try:
    self.readonly_fields = self.staff_self_readonly_fields
    response = super(MyUserAdmin, self).change_view(request, object_id, form_url, extra_context, *args, **kwargs)
finally:
    # Reset fieldsets to its original value
    self.fieldsets = UserAdmin.fieldsets
Run Code Online (Sandbox Code Playgroud)

为了避免这种竞争条件(我认为提高了解决方案的整体质量),我们可以直接覆盖get_fieldsets()get_readonly_fields()方法:

class UserAdmin(BaseUserAdmin):
    staff_fieldsets = (
        (None, {'fields': ('username')}),
        ('Personal info', {'fields': ('first_name', 'last_name', 'email')}),
        # No permissions
        ('Important dates', {'fields': ('last_login', 'date_joined')}),
    )
    staff_readonly_fields = ('username', 'first_name', 'last_name', 'email', 'last_login', 'date_joined')

    def get_fieldsets(self, request, obj=None):
        if not request.user.is_superuser:
            return self.staff_fieldsets
        else:
            return super(UserAdmin, self).get_fieldsets(request, obj)

    def get_readonly_fields(self, request, obj=None):
        if not request.user.is_superuser:
            return self.staff_readonly_fields
        else:
            return super(UserAdmin, self).get_readonly_fields(request, obj)
Run Code Online (Sandbox Code Playgroud)