1
|
|
|
# -*- coding: utf-8 -*- |
2
|
|
|
|
3
|
|
|
from django import forms |
4
|
|
|
from django.contrib import admin |
5
|
|
|
from django.contrib.admin.widgets import FilteredSelectMultiple |
6
|
|
|
from django.contrib.auth import get_user_model |
7
|
|
|
from django.contrib.auth.admin import GroupAdmin, UserAdmin, sensitive_post_parameters_m |
8
|
|
|
from django.contrib.auth.forms import UserChangeForm |
9
|
|
|
from django.contrib.auth.models import Group, Permission |
10
|
|
|
from django.http import HttpResponseForbidden, HttpResponseRedirect |
11
|
|
|
from django.urls import reverse |
12
|
|
|
from django.utils.text import capfirst |
13
|
|
|
from django.utils.translation import gettext_lazy as _ |
14
|
|
|
|
15
|
|
|
from tcms.utils.user import delete_user |
16
|
|
|
|
17
|
|
|
User = get_user_model() # pylint: disable=invalid-name |
18
|
|
|
|
19
|
|
|
|
20
|
|
|
class MyUserChangeForm(UserChangeForm): |
21
|
|
|
""" |
22
|
|
|
Enforces unique user emails. |
23
|
|
|
""" |
24
|
|
|
|
25
|
|
|
email = forms.EmailField(required=True) |
26
|
|
|
|
27
|
|
|
def clean_email(self): |
28
|
|
|
query_set = User.objects.filter(email=self.cleaned_data["email"]) |
29
|
|
|
if self.instance: |
30
|
|
|
query_set = query_set.exclude(pk=self.instance.pk) |
31
|
|
|
if query_set.count(): |
32
|
|
|
raise forms.ValidationError(_("This email address is already in use")) |
33
|
|
|
|
34
|
|
|
return self.cleaned_data["email"] |
35
|
|
|
|
36
|
|
|
|
37
|
|
|
def _modifying_myself(request, object_id): |
38
|
|
|
return request.user.pk == int(object_id) |
39
|
|
|
|
40
|
|
|
|
41
|
|
|
class GroupAdminForm(forms.ModelForm): |
42
|
|
|
class Meta: |
43
|
|
|
model = Group |
44
|
|
|
fields = ["name", "permissions"] |
45
|
|
|
|
46
|
|
|
users = forms.ModelMultipleChoiceField( |
47
|
|
|
queryset=User.objects.all(), |
48
|
|
|
required=False, |
49
|
|
|
widget=FilteredSelectMultiple("users", False), |
50
|
|
|
) |
51
|
|
|
|
52
|
|
|
def __init__(self, *args, **kwargs): |
53
|
|
|
super().__init__(*args, **kwargs) |
54
|
|
|
self.fields["users"].label = capfirst(_("users")) |
55
|
|
|
if self.instance.pk: |
56
|
|
|
self.fields["users"].initial = self.instance.user_set.all() |
57
|
|
|
|
58
|
|
|
def save(self, commit=True): |
59
|
|
|
instance = super().save(commit=commit) |
60
|
|
|
instance.save() |
61
|
|
|
|
62
|
|
|
self.instance.user_set.set(self.cleaned_data["users"]) |
63
|
|
|
self.save_m2m() |
64
|
|
|
|
65
|
|
|
return instance |
66
|
|
|
|
67
|
|
|
|
68
|
|
|
class KiwiUserAdmin(UserAdmin): |
69
|
|
|
list_display = UserAdmin.list_display + ( |
70
|
|
|
"is_active", |
71
|
|
|
"is_superuser", |
72
|
|
|
"date_joined", |
73
|
|
|
"last_login", |
74
|
|
|
) |
75
|
|
|
ordering = ["-pk"] # same as -date_joined |
76
|
|
|
|
77
|
|
|
# override standard form and make the email address unique |
78
|
|
|
# even when adding users via admin panel |
79
|
|
|
form = MyUserChangeForm |
80
|
|
|
|
81
|
|
|
def has_change_permission(self, request, obj=None): |
82
|
|
|
return request.user.is_superuser or obj is not None |
83
|
|
|
|
84
|
|
|
def has_view_permission(self, request, obj=None): |
85
|
|
|
if _modifying_myself(request, getattr(obj, "pk", 0)): |
86
|
|
|
return True |
87
|
|
|
|
88
|
|
|
return super().has_view_permission(request, obj) |
89
|
|
|
|
90
|
|
|
# pylint: disable=too-many-arguments |
91
|
|
|
def render_change_form( |
92
|
|
|
self, request, context, add=False, change=False, form_url="", obj=None |
93
|
|
|
): |
94
|
|
|
if not self.has_view_permission(request, obj): |
95
|
|
|
return HttpResponseForbidden() |
96
|
|
|
|
97
|
|
|
if obj and not ( |
98
|
|
|
_modifying_myself(request, obj.pk) or request.user.is_superuser |
99
|
|
|
): |
100
|
|
|
context.update( |
101
|
|
|
{ |
102
|
|
|
"show_save": False, |
103
|
|
|
"show_save_and_continue": False, |
104
|
|
|
} |
105
|
|
|
) |
106
|
|
|
return super().render_change_form( |
107
|
|
|
request, context, add=add, change=change, form_url=form_url, obj=obj |
108
|
|
|
) |
109
|
|
|
|
110
|
|
|
def get_readonly_fields(self, request, obj=None): |
111
|
|
|
if request.user.is_superuser: |
112
|
|
|
return super().get_readonly_fields(request, obj) |
113
|
|
|
|
114
|
|
|
readonly_fields = [ |
115
|
|
|
"username", |
116
|
|
|
"is_staff", |
117
|
|
|
"is_active", |
118
|
|
|
"is_superuser", |
119
|
|
|
"last_login", |
120
|
|
|
"date_joined", |
121
|
|
|
"groups", |
122
|
|
|
"user_permissions", |
123
|
|
|
] |
124
|
|
|
if obj and not _modifying_myself(request, obj.pk): |
125
|
|
|
readonly_fields.extend(["first_name", "last_name", "email"]) |
126
|
|
|
|
127
|
|
|
return readonly_fields |
128
|
|
|
|
129
|
|
|
def get_fieldsets(self, request, obj=None): |
130
|
|
|
# super-user adding new account |
131
|
|
|
if not obj and request.user.is_superuser: |
132
|
|
|
return super().get_fieldsets(request, obj) |
133
|
|
|
|
134
|
|
|
first_fieldset_fields = ("username",) |
135
|
|
|
if obj and _modifying_myself(request, obj.pk): |
136
|
|
|
first_fieldset_fields = first_fieldset_fields + ("password",) |
137
|
|
|
|
138
|
|
|
remaining_fieldsets = ( |
139
|
|
|
(_("Personal info"), {"fields": ("first_name", "last_name", "email")}), |
140
|
|
|
(_("Permissions"), {"fields": ("is_active", "groups")}), |
141
|
|
|
) |
142
|
|
|
|
143
|
|
|
if request.user.is_superuser: |
144
|
|
|
field_sets = super().get_fieldsets(request, obj) |
145
|
|
|
if field_sets[0][0] is None and "password" in field_sets[0][1]["fields"]: |
146
|
|
|
remaining_fieldsets = field_sets[1:] |
147
|
|
|
|
148
|
|
|
return ((None, {"fields": first_fieldset_fields}),) + remaining_fieldsets |
149
|
|
|
|
150
|
|
|
@sensitive_post_parameters_m |
151
|
|
|
def user_change_password( |
152
|
|
|
self, request, id, form_url="" |
153
|
|
|
): # pylint: disable=redefined-builtin |
154
|
|
|
return HttpResponseRedirect(reverse("admin:password_change")) |
155
|
|
|
|
156
|
|
|
@admin.options.csrf_protect_m |
157
|
|
|
def delete_view(self, request, object_id, extra_context=None): |
158
|
|
|
if not _modifying_myself(request, object_id): |
159
|
|
|
return super().delete_view(request, object_id, extra_context) |
160
|
|
|
|
161
|
|
|
# allow deletion of the user own account |
162
|
|
|
permission = Permission.objects.get( |
163
|
|
|
content_type__app_label="auth", codename="delete_user" |
164
|
|
|
) |
165
|
|
|
try: |
166
|
|
|
request.user.user_permissions.add(permission) |
167
|
|
|
return super().delete_view(request, object_id, extra_context) |
168
|
|
|
finally: |
169
|
|
|
request.user.user_permissions.remove(permission) |
170
|
|
|
|
171
|
|
|
def response_delete(self, request, obj_display, obj_id): |
172
|
|
|
result = super().response_delete(request, obj_display, obj_id) |
173
|
|
|
|
174
|
|
|
if not _modifying_myself(request, obj_id): |
175
|
|
|
return result |
176
|
|
|
|
177
|
|
|
# user doesn't exist anymore so go to the index page |
178
|
|
|
# instead of returning to the user admin page |
179
|
|
|
return HttpResponseRedirect(reverse("core-views-index")) |
180
|
|
|
|
181
|
|
|
def has_delete_permission(self, request, obj=None): |
182
|
|
|
# allow to delete yourself without having 'delete' permission |
183
|
|
|
# explicitly assigned |
184
|
|
|
if _modifying_myself(request, getattr(obj, "pk", 0)): |
185
|
|
|
return True |
186
|
|
|
|
187
|
|
|
return super().has_delete_permission(request, obj) |
188
|
|
|
|
189
|
|
|
def delete_model(self, request, obj): |
190
|
|
|
delete_user(obj) |
191
|
|
|
|
192
|
|
|
|
193
|
|
|
class KiwiGroupAdmin(GroupAdmin): |
194
|
|
|
form = GroupAdminForm |
195
|
|
|
|
196
|
|
|
def has_delete_permission(self, request, obj=None): |
197
|
|
|
if obj and obj.name in ["Tester", "Administrator"]: |
198
|
|
|
return False |
199
|
|
|
return super().has_delete_permission(request, obj) |
200
|
|
|
|
201
|
|
|
def get_fields(self, request, obj=None): |
202
|
|
|
fields = super().get_fields(request, obj=obj) |
203
|
|
|
name_index = fields.index("name") |
204
|
|
|
|
205
|
|
|
# make sure Name is always the first field |
206
|
|
|
if name_index > 0: |
207
|
|
|
del fields[name_index] |
208
|
|
|
fields.insert(0, "name") |
209
|
|
|
|
210
|
|
|
return fields |
211
|
|
|
|
212
|
|
|
def get_readonly_fields(self, request, obj=None): |
213
|
|
|
readonly_fields = super().get_readonly_fields(request, obj) |
214
|
|
|
|
215
|
|
|
if obj and obj.name in ["Tester", "Administrator"]: |
216
|
|
|
readonly_fields = readonly_fields + ("name",) |
217
|
|
|
|
218
|
|
|
return readonly_fields |
219
|
|
|
|
220
|
|
|
|
221
|
|
|
# user admin extended functionality |
222
|
|
|
admin.site.unregister(User) |
223
|
|
|
admin.site.register(User, KiwiUserAdmin) |
224
|
|
|
admin.site.unregister(Group) |
225
|
|
|
admin.site.register(Group, KiwiGroupAdmin) |
226
|
|
|
|