# -*- coding: utf-8 -*-
from django import forms
from django.contrib import admin, messages
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import GroupAdmin, UserAdmin, sensitive_post_parameters_m
from django.contrib.auth.models import Group, Permission
from django.core.exceptions import PermissionDenied
from django.http import HttpResponseRedirect
from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext_lazy as _
from tcms.utils.user import delete_user
User = get_user_model() # pylint: disable=invalid-name
def _modifying_myself(request, object_id):
return request.user.pk == int(object_id)
[docs]
class KiwiUserAdmin(UserAdmin):
list_display = UserAdmin.list_display + (
"is_active",
"is_superuser",
"date_joined",
"last_login",
)
ordering = ["-pk"] # same as -date_joined
[docs]
def has_view_permission(self, request, obj=None):
return _modifying_myself(
request, getattr(obj, "pk", 0)
) or super().has_view_permission(request, obj)
[docs]
def has_change_permission(self, request, obj=None):
return _modifying_myself(
request, getattr(obj, "pk", 0)
) or super().has_change_permission(request, obj)
[docs]
def has_delete_permission(self, request, obj=None):
return _modifying_myself(
request, getattr(obj, "pk", 0)
) or super().has_delete_permission(request, obj)
# pylint: disable=too-many-arguments,too-many-positional-arguments
[docs]
def get_readonly_fields(self, request, obj=None):
# adding new user
if not obj:
return super().get_readonly_fields(request, obj)
readonly_fields = [
"username",
"last_login",
"date_joined",
"email",
]
# only other superusers can set the is_superuser flag
if not request.user.is_superuser:
readonly_fields.append("is_superuser")
# if you have explicit change_user permission you can modify these fields
# however users are not able to give themselves elevated permissions
if not self.has_change_permission(request, None):
readonly_fields.extend(
[
"is_staff",
"is_active",
"groups",
"user_permissions",
]
)
# lastly users can't modify others unless they have the expolicit permission
if not _modifying_myself(request, obj.pk):
readonly_fields.extend(["first_name", "last_name", "email"])
return readonly_fields
[docs]
def get_fieldsets(self, request, obj=None):
# adding new account b/c we have permissions
if not obj and self.has_add_permission(request):
return super().get_fieldsets(request, obj)
first_fieldset_fields = ("username",)
if obj and _modifying_myself(request, obj.pk):
first_fieldset_fields += ("password",)
remaining_fieldsets = (
(_("Personal info"), {"fields": ("first_name", "last_name", "email")}),
(
_("Permissions"),
{
"fields": (
"is_active",
"is_staff",
"is_superuser",
"groups",
"user_permissions",
)
},
),
)
if request.user.is_superuser:
field_sets = super().get_fieldsets(request, obj)
if field_sets[0][0] is None and "password" in field_sets[0][1]["fields"]:
remaining_fieldsets = field_sets[1:]
return ((None, {"fields": first_fieldset_fields}),) + remaining_fieldsets
[docs]
@sensitive_post_parameters_m
def user_change_password(
self, request, id, form_url=""
): # pylint: disable=redefined-builtin
if _modifying_myself(request, id):
return HttpResponseRedirect(reverse("admin:password_change"))
raise PermissionDenied
[docs]
@admin.options.csrf_protect_m
def delete_view(self, request, object_id, extra_context=None):
user = User.objects.get(pk=object_id)
# check whether the last superuser is being deleted
if user.is_superuser and User.objects.filter(is_superuser=True).count() == 1:
messages.add_message(
request,
messages.ERROR,
_("This is the last superuser, it cannot be deleted!"),
)
return HttpResponseRedirect(
reverse("admin:auth_user_change", args=[user.pk])
)
if not _modifying_myself(request, object_id):
return super().delete_view(request, object_id, extra_context)
# allow deletion of the user own account
permission = Permission.objects.get(
content_type__app_label="auth", codename="delete_user"
)
try:
request.user.user_permissions.add(permission)
return super().delete_view(request, object_id, extra_context)
finally:
request.user.user_permissions.remove(permission)
[docs]
def response_delete(self, request, obj_display, obj_id):
result = super().response_delete(request, obj_display, obj_id)
if not _modifying_myself(request, obj_id):
return result
# user doesn't exist anymore so go to the login page
return HttpResponseRedirect(reverse("tcms-login"))
[docs]
def delete_model(self, request, obj):
delete_user(obj)
[docs]
class KiwiGroupAdmin(GroupAdmin):
form = GroupAdminForm
[docs]
def has_delete_permission(self, request, obj=None):
if obj and obj.name in ["Tester", "Administrator"]:
return False
return super().has_delete_permission(request, obj)
[docs]
def get_fields(self, request, obj=None):
fields = super().get_fields(request, obj=obj)
name_index = fields.index("name")
# make sure Name is always the first field
if name_index > 0:
del fields[name_index]
fields.insert(0, "name")
return fields
[docs]
def get_readonly_fields(self, request, obj=None):
readonly_fields = super().get_readonly_fields(request, obj)
if obj and obj.name in ["Tester", "Administrator"]:
readonly_fields += ("name",)
return readonly_fields
# user admin extended functionality
admin.site.unregister(User)
admin.site.register(User, KiwiUserAdmin)
admin.site.unregister(Group)
admin.site.register(Group, KiwiGroupAdmin)