2010-02-19 11 views
14

Tengo un sitio django con una gran base de clientes. Me gustaría dar a nuestro departamento de servicio al cliente la capacidad de alterar cuentas de usuario normales, haciendo cosas como cambiar contraseñas, direcciones de correo electrónico, etc. Sin embargo, si concedo a alguien el permiso incorporado auth | user | Can change user, obtienen la capacidad de establecer la bandera is_superuser en cualquier cuenta, incluida la suya. (!!!)¿Cómo evito la escalada de permisos en el administrador de Django cuando otorgo el permiso de "cambio de usuario"?

¿Cuál es la mejor manera de eliminar esta opción para el personal no superusuario? Estoy seguro de que implica la subclasificación de django.contrib.auth.forms.UserChangeForm y engancharla en mi objeto ya personalizado UserAdmin ... de alguna manera. Pero no puedo encontrar ninguna documentación sobre cómo hacer esto, y todavía no entiendo lo interno lo suficiente.

Respuesta

19

obtienen la capacidad de establecer el indicador is_superuser en cualquier cuenta, incluidas las suyas. (!!!)

No sólo esto, sino que también adquieren la capacidad de darse a sí mismos ningún permiso uno por uno, mismo efecto ...

Estoy seguro de que implica la subclasificación de Django .contrib.auth.forms.UserChangeForm

Bueno, no necesariamente. El formulario que se ve en la página de cambio del administrador de django se crea dinámicamente por la aplicación de administración y se basa en UserChangeForm, pero esta clase apenas agrega validación de expresiones regulares al campo username.

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

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

Pero el problema aquí es que esta restricción se aplicará a todos los usuarios. Si esto no es lo que desea, podría, por ejemplo, anular change_view para comportarse de manera diferente según el permiso 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) 
+0

Que hizo el truco . ¡Gracias por una respuesta tan completa! –

+0

Como uso Grupos para administrar permisos, también eliminé la sección Grupos de 'staff_fieldsets'. –

+0

Gracias! ¡Esto me ayudó mucho! Sin embargo, a Django 1.1.2 no pareció gustarle el "_" que tenías antes de la información personal y el resto. – Tyug

0

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) 
0

muchas gracias a Clément. Lo que se me ocurrió al hacer lo mismo para mi sitio es que necesitaba, además, que todos los campos sean de solo lectura para los usuarios que no sean ustedes mismos. Así basándose en la respuesta de Clemente I addeed campos de sólo lectura y escondite campo de contraseña cuando no se están viendo 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) 
2

La parte de abajo de la respuesta aceptada tiene una condición de carrera en la que si dos usuarios del personal tratan de acceder al formulario de administración al mismo tiempo, , uno de ellos puede obtener la forma 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) 
Cuestiones relacionadas