代码之家  ›  专栏  ›  技术社区  ›  David Eyk

如何在授予“用户更改”权限时防止django admin中的权限升级?

  •  18
  • David Eyk  · 技术社区  · 16 年前

    我有一个django网站,有很大的客户群。我想让我们的客户服务部门有能力改变正常的用户帐户,做诸如更改密码、电子邮件地址等事情。但是,如果我授予某人内置的 auth | user | Can change user 允许,他们可以设置 is_superuser 不管什么原因,包括他们自己。(!!!!)

    对于非超级用户员工,删除此选项的最佳方法是什么?我相信这涉及到子类化 django.contrib.auth.forms.UserChangeForm 把它和我的习惯联系起来 UserAdmin 对象…不知何故。但我找不到任何关于如何做到这一点的文档,而且我还不太了解其中的内部内容。

    5 回复  |  直到 16 年前
        1
  •  19
  •   seddonym    11 年前

    他们可以在任何帐户(包括他们自己的帐户)上设置IS U超级用户标志。(!!!!)

    不仅如此,他们还获得了一个接一个给自己任何权限的能力,同样的效果……

    我确信它涉及到django.contrib.auth.forms.userchangeform的子类化。

    嗯,不一定。您在django的admin的change页面中看到的表单是由管理应用程序动态创建的,并且基于 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',)}),
        )
    

    但这里的问题是,这个限制将适用于所有用户。如果这不是你想要的,你可以 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)
    
        2
  •  5
  •   dkmita    9 年前

    接受答案的下面部分有一个竞赛条件,如果两个工作人员用户试图同时访问管理表单,其中一个用户可能会获得超级用户表单。

    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
    

    为了避免这种竞争条件(并且在我看来提高了解决方案的整体质量),我们可以覆盖 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)
    
        3
  •  1
  •   Unicorn    10 年前

    多亏了克莱门特。当我为我的网站做同样的事情时,我想到的是我需要另外使所有的字段都只为你自己以外的用户读。因此,基于clment的答案,我添加了readonly字段和password字段隐藏在查看not self时

    class MyUserAdmin(UserAdmin):
        model = User
        staff_self_fieldsets = (
            (None, {'fields': ('username', 'password')}),
            (_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
            # No permissions
            (_('Important dates'), {'fields': ('last_login', 'date_joined')}),
        )
    
        staff_other_fieldsets = (
            (None, {'fields': ('username', )}),
            (_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
            # No permissions
            (_('Important dates'), {'fields': ('last_login', 'date_joined')}),
        )
    
        staff_self_readonly_fields = ('last_login', 'date_joined')
    
        def change_view(self, request, object_id, form_url='', extra_context=None, *args, **kwargs):
            # for non-superuser
            if not request.user.is_superuser:
                try:
                    if int(object_id) != request.user.id:
                        self.readonly_fields = User._meta.get_all_field_names()
                        self.fieldsets = self.staff_other_fieldsets
                    else:
                        self.readonly_fields = self.staff_self_readonly_fields
                        self.fieldsets = self.staff_self_fieldsets
    
                    response = super(MyUserAdmin, self).change_view(request, object_id, form_url, extra_context, *args, **kwargs)
                except:
                    logger.error('Admin change view error. Returned all readonly fields')
    
                    self.fieldsets = self.staff_other_fieldsets
                    self.readonly_fields = ('first_name', 'last_name', 'email', 'username', 'password', 'last_login', 'date_joined')
                    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
                    self.readonly_fields = UserAdmin.readonly_fields
                return response
            else:
                return super(MyUserAdmin, self).change_view(request, object_id, form_url, extra_context, *args, **kwargs)
    
        4
  •  0
  •   bopajdowski    16 年前

    Django 1.1的完整代码(仅限于员工(非超级用户)的基本用户信息)

    from django.contrib.auth.models import User
    from django.utils.translation import ugettext_lazy as _
    
    
    class MyUserAdmin(UserAdmin):
       my_fieldsets = (
           (None, {'fields': ('username', 'password')}),
           (_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
       )
    
       def change_view(self, request, object_id, extra_context=None):
           # for non-superuser
           print 'test'
           if not request.user.is_superuser:
               self.fieldsets = self.my_fieldsets
               response = UserAdmin.change_view(self, request, object_id,
    extra_context=None)
               return response
           else:
               return UserAdmin.change_view(self, request, object_id,
    extra_context=None)
    
    
    admin.site.unregister(User)
    admin.site.register(User, MyUserAdmin)
    
        5
  •  0
  •   Eric Woudenberg    7 年前

    这个方法是从网上的几个有用的提示中总结出来的。在本例中,我们将修改useradmin,以便对于具有用户添加/更改权限的非超级用户员工,他们可以授予另一个用户的唯一权限和组是员工已经拥有的权限和组。

    (适用于Django 1.11)

    from django.contrib.auth.admin import UserAdmin, User
    from django.contrib import admin
    
    class RestrictedUserAdmin(UserAdmin):
        model = User
    
        def formfield_for_dbfield(self, db_field, **kwargs):
            field = super(RestrictedUserAdmin, self).formfield_for_dbfield(db_field, **kwargs)
            user = kwargs['request'].user
            if not user.is_superuser:
                if db_field.name == 'groups':
                    field.queryset = field.queryset.filter(id__in=[i.id for i in user.groups.all()])
                if db_field.name == 'user_permissions':
                    field.queryset = field.queryset.filter(id__in=[i.id for i in user.user_permissions.all()])
                if db_field.name == 'is_superuser':
                    field.widget.attrs['disabled'] = True
            return field
    
    admin.site.unregister(User)
    admin.site.register(User, RestrictedUserAdmin)
    

    如果用户被授予更改组的权限,那么groupadmin也应该这样做。