Come posso evitare il permesso escalation di Django di amministrazione nel concedere l'autorizzazione “Cambia utente”?

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

Domanda

Ho un sito Django con una vasta clientela. Vorrei dare il nostro servizio clienti la capacità di alterare le normali account utente, fare le cose come cambiare le password, indirizzi e-mail, ecc, tuttavia, se mi concedo qualcuno il built-nel permesso auth | user | Can change user, guadagnano la possibilità di impostare il flag is_superuser in nessun caso, compreso il proprio. (!!!)

Qual è il modo migliore per rimuovere questa opzione per il personale non-superuser? Sono sicuro che si tratta di sottoclassi django.contrib.auth.forms.UserChangeForm e agganciandolo nella mia oggetto UserAdmin già personalizzato ... in qualche modo. Ma non riesco a trovare alcuna documentazione su come fare questo, e non ho ancora a capire i meccanismi interni abbastanza bene.

È stato utile?

Soluzione

  

guadagnano la possibilità di impostare il flag is_superuser su qualsiasi account, compreso il proprio. (!!!)

Non solo questo, ma anche acquisire la capacità di darsi le autorizzazioni uno per uno, stesso effetto ...

  

Sono sicuro che si tratta di creazione di sottoclassi django.contrib.auth.forms.UserChangeForm

Beh, non necessariamente. Il modulo che vedete nella pagina di cambio di amministrazione di Django viene dinamicamente creata dall'applicazione di amministrazione, e sulla base di UserChangeForm, ma questa classe aggiunge a malapena convalida regex al campo username.

  

e agganciandolo nella mia oggetto UserAdmin già personalizzato ...

Un UserAdmin personalizzato è il modo di andare qui. In sostanza, si vuole modificare la proprietà fieldsets a qualcosa di simile:

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

Ma il problema qui è che questa limitazione si applicherà a tutti gli utenti. Se questo non è quello che si vuole è, si potrebbe ad esempio ignorare change_view a comportarsi in modo diverso a seconda del consenso degli utenti. Frammento di codice:

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)

Altri suggerimenti

La parte di sotto della risposta accettata è una condizione di competizione in cui se due utenti del personale cercano di accedere al modulo di amministrazione, allo stesso tempo, una di esse può ottenere la forma superutente.

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

Per evitare questa condizione di gara (e, a mio parere migliorare la qualità complessiva della soluzione), siamo in grado di sostituire direttamente i metodi get_fieldsets() e 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)

Gran grazie a Clément. Quello che mi è venuta quando si fa lo stesso per il mio sito è che avevo bisogno inoltre di rendere tutti i campi di sola lettura per gli utenti di altri che di sé. Quindi, basandosi su risposta di Clemente I addeed campi di sola lettura e campo password nascondersi durante la visualizzazione non 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)

codice completo per Django 1.1 (limitato alle informazioni degli utenti di base per il personale (non superuser))

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)

Questo approccio è stato messo insieme da diversi suggerimenti utili sul web. In questo caso stiamo modificando UserAdmin in modo che, per il personale non superuser con l'utente aggiungere il permesso / cambio, gli unici permessi e gruppi possono concedere un altro utente sono quelli del membro del personale ha già.

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

Questo dovrebbe altresì essere fatto per GroupAdmin se un utente è dato il permesso di modificare i gruppi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top