¿Cómo puedo evitar una escalada en el permiso de Django administrador cuando la concesión de permiso “usuario cambio”?

StackOverflow https://stackoverflow.com/questions/2297377

Pregunta

Tengo un sitio Django con una gran base de clientes. Me gustaría dar nuestro departamento de servicio al cliente la posibilidad de modificar las cuentas de usuario normales, haciendo cosas como el cambio de contraseñas, direcciones de correo electrónico, etc. Sin embargo, si alguien Concedo la incorporada en el permiso auth | user | Can change user, adquieren la capacidad para establecer el indicador is_superuser por ningún motivo, incluido el propio. (!!!)

¿Cuál es la mejor manera de eliminar esta opción para el personal no superusuario? Estoy seguro de que implica la subclasificación django.contrib.auth.forms.UserChangeForm y enganchándolo en mi objeto UserAdmin ya de por encargo ... de alguna manera. Pero no puedo encontrar ninguna documentación sobre cómo hacer esto, y yo todavía no comprenden la parte interna lo suficientemente bien.

¿Fue útil?

Solución

  

se obtiene la capacidad para establecer el indicador is_superuser en cualquier cuenta, incluido el propio. (!!!)

No sólo esto, sino que también adquieren la capacidad de darse los permisos de uno en uno, mismo efecto ...

  

Estoy seguro de que se trata de subclases django.contrib.auth.forms.UserChangeForm

Bueno, no necesariamente. La forma que se ve en la página de cambio de administración de Django es dinámica creada por la aplicación de administración, y en base a UserChangeForm, pero esta clase apenas añade la validación de expresiones regulares al campo username.

  

y enganchándolo en mi ya de por encargo objeto UserAdmin ...

A UserAdmin costumbre es el camino a seguir aquí. Básicamente, desea cambiar la propiedad fieldsets a algo así:

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',)}),
    )

Sin embargo, el problema aquí es que esta restricción se aplicará a todos los usuarios. Si esto no es lo que quiere, se puede por ejemplo change_view anulación para comportarse de forma diferente en función de la autorización de los usuarios. Fragmento de código:

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)

Otros consejos

La continuación parte de la respuesta aceptada tiene una condición de carrera en la que si dos usuarios intentan acceder a personal de la forma de administración, al mismo tiempo, uno de ellos puede obtener el formulario de superusuario.

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

Para evitar esta condición de carrera (y en mi opinión mejorar la calidad general de la solución), podemos reemplazar los métodos get_fieldsets() y get_readonly_fields() directamente:

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)

Muchas gracias a Clément. Lo que se me ocurrió cuando se hace lo mismo para mi sitio es que necesitaba, además, para que todos los campos de sólo lectura para los usuarios que otros que sí. Así basándose en la respuesta de Clemente I addeed campos de sólo lectura y escondite campo de contraseña cuando se ve no se auto

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)

código completo de Django 1.1 (limitado a la información de usuario básica para el personal (no superusuarios))

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)

Este enfoque fue elaborado a partir de varios consejos útiles en la web. En este caso estamos modificando UserAdmin de manera que, para el personal no superusuario con el usuario agrega / permiso de modificación, los únicos permisos y grupos que pueden conceder otro usuario son los que el miembro del personal que ya tiene.

(por 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)

Esto debe hacerse del mismo modo para GroupAdmin si un usuario se le da permiso para grupos de cambio.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top