كيف يمكنني منع تصعيد الإذن في مسؤول Django عند منح إذن "تغيير المستخدم"؟
-
21-09-2019 - |
سؤال
لدي موقع Django مع قاعدة عملاء كبيرة. أود أن أعطي قسم خدمة العملاء لدينا القدرة على تغيير حسابات المستخدم العادية ، والقيام بأشياء مثل تغيير كلمات المرور وعناوين البريد الإلكتروني ، وما إلى ذلك ، إذا منحت شخصًا ما مدمجًا في شخص ما auth | user | Can change user
إذن ، يكتسبون القدرة على تعيين is_superuser
العلم على أي حساب ، بما في ذلك خاصة بهم. (!!!)
ما هي أفضل طريقة لإزالة هذا الخيار للموظفين غير الخاطفين؟ أنا متأكد من أنه ينطوي على التصنيف الفرعي django.contrib.auth.forms.UserChangeForm
وربطه في بلدي بالفعل UserAdmin
كائن ... بطريقة ما. لكن لا يمكنني العثور على أي وثائق حول كيفية القيام بذلك ، ولم أفهم بعد الأجزاء الداخلية جيدًا بما فيه الكفاية.
المحلول
يكتسبون القدرة على تعيين علامة is_superuser على أي حساب ، بما في ذلك خاصة بهم. (!!!)
ليس هذا فحسب ، بل يكتسبون أيضًا القدرة على إعطاء أنفسهم أي أذونات واحدة تلو الأخرى ، نفس التأثير ...
أنا متأكد من أنه يتضمن تصنيف django.contrib.auth.forms.userChangeform
حسنا ، ليس بالضرورة. يتم إنشاء النموذج الذي تراه في صفحة تغيير مسؤول Django ديناميكيًا بواسطة تطبيق المسؤول ، واستنادًا إلى UserChangeForm
, ، لكن هذه الفئة بالكاد تضيف التحقق من صحة regex إلى 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)
نصائح أخرى
الجزء أدناه من الإجابة المقبولة لديه حالة سباق حيث إذا حاول اثنان من الموظفين الوصول إلى نموذج المسؤول في نفس الوقت ، فقد يحصل أحدهما على نموذج Superuser.
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)
شكرا جزيلا ل Clément. ما توصلت إليه عند فعل الشيء نفسه لموقعي هو أنني بحاجة بالإضافة إلى ذلك لجعل جميع الحقول قراءة للمستخدمين غير نفسك. لذا ، فإن الإجابة على Clément ، أقوم بإضافة حقول ReadOnly وحقل كلمة المرور المختبئين عند مشاهدة غير الذات
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)
الرمز الكامل لـ 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)
تم وضع هذا النهج من عدة نصائح مفيدة على الويب. في هذه الحالة ، نقوم بتعديل 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 إذا تم منح المستخدم إذنًا لتغيير المجموعات.