template - ¿Cómo evito la escalada de permisos en el administrador de Django cuando se concede el permiso de "cambio de usuario"?
django user model (5)
obtienen la capacidad de establecer la marca is_superuser en cualquier cuenta, incluida la propia. (!!!)
No solo esto, también obtienen la capacidad de otorgarse permisos uno por uno, el mismo efecto ...
Estoy seguro de que implica subclasificar django.contrib.auth.forms.UserChangeForm
Bueno, no necesariamente. El formulario que se ve en la página de cambios del administrador de django es creado dinámicamente por la aplicación de administración y se basa en UserChangeForm
, pero esta clase apenas agrega validación de UserChangeForm
regulares al campo de username
.
y conectándolo a mi objeto UserAdmin ya personalizado ...
Un UserAdmin
personalizado es el camino a seguir aquí. Básicamente, quieres cambiar la propiedad de los 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 dependiendo del 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)
Tengo un sitio de django con una gran base de clientes. Me gustaría dar a nuestro departamento de servicio al cliente la capacidad de alterar las cuentas de usuario normales, hacer cosas como cambiar contraseñas, direcciones de correo electrónico, etc. Sin embargo, si otorgo a alguien la auth | user | Can change user
incorporada auth | user | Can change user
auth | user | Can change user
auth | user | Can change user
permiso del auth | user | Can change user
y pueden establecer la is_superuser
en cualquier cuenta, incluida la propia. (!!!)
¿Cuál es la mejor manera de eliminar esta opción para el personal que no es superusuario? Estoy seguro de que implica subclasificar django.contrib.auth.forms.UserChangeForm
y conectarlo a mi objeto UserAdmin
ya personalizado ... de alguna manera. Pero no puedo encontrar ninguna documentación sobre cómo hacer esto, y todavía no entiendo lo interno lo suficientemente bien.
Código completo para django 1.1 (limitado a la información básica del usuario 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 para que, para el personal sin superusuario con permiso de agregar / cambiar usuario, los únicos permisos y grupos que pueden otorgar a otro usuario sean los que el miembro del personal ya tiene.
(para 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 también debe hacerse para GroupAdmin si un usuario tiene permiso para cambiar de grupo.
La siguiente parte de la respuesta aceptada tiene una condición de carrera en la que si dos usuarios del personal intentan acceder al formulario 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 anular los 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ó al hacer lo mismo para mi sitio es que, además, necesitaba que todos los campos fueran de solo lectura para los usuarios que no sean yo. Así que basándome en la respuesta de Clément, escribí campos de solo lectura y campos de contraseñas que se ocultan cuando no se ve a uno mismo.
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)