1
|
|
|
# -*- coding: utf-8 -*- |
2
|
|
|
|
3
|
|
|
from django import forms |
4
|
|
|
from django.urls import reverse |
5
|
|
|
from django.contrib import admin |
6
|
|
|
from django.http import HttpResponseRedirect |
7
|
|
|
from django.contrib.auth import get_user_model |
8
|
|
|
from django.contrib.auth.models import Permission |
9
|
|
|
from django.contrib.auth.forms import UserChangeForm |
10
|
|
|
from django.utils.translation import ugettext_lazy as _ |
11
|
|
|
from django.contrib.auth.admin import UserAdmin, sensitive_post_parameters_m |
12
|
|
|
|
13
|
|
|
from tcms.utils.user import delete_user |
14
|
|
|
|
15
|
|
|
User = get_user_model() # pylint: disable=invalid-name |
16
|
|
|
|
17
|
|
|
|
18
|
|
|
class MyUserChangeForm(UserChangeForm): |
19
|
|
|
""" |
20
|
|
|
Enforces unique user emails. |
21
|
|
|
""" |
22
|
|
|
email = forms.EmailField(required=True) |
23
|
|
|
|
24
|
|
|
def clean_email(self): |
25
|
|
|
query_set = User.objects.filter(email=self.cleaned_data['email']) |
26
|
|
|
if self.instance: |
27
|
|
|
query_set = query_set.exclude(pk=self.instance.pk) |
28
|
|
|
if query_set.count(): |
29
|
|
|
raise forms.ValidationError(_('This email address is already in use')) |
30
|
|
|
|
31
|
|
|
return self.cleaned_data['email'] |
32
|
|
|
|
33
|
|
|
|
34
|
|
|
def _modifying_myself(request, object_id): |
35
|
|
|
return request.user.pk == int(object_id) |
36
|
|
|
|
37
|
|
|
|
38
|
|
|
class KiwiUserAdmin(UserAdmin): |
39
|
|
|
list_display = UserAdmin.list_display + ('is_superuser', 'date_joined', 'last_login') |
40
|
|
|
ordering = ['-pk'] # same as -date_joined |
41
|
|
|
|
42
|
|
|
# override standard form and make the email address unique |
43
|
|
|
# even when adding users via admin panel |
44
|
|
|
form = MyUserChangeForm |
45
|
|
|
|
46
|
|
|
def has_change_permission(self, request, obj=None): |
47
|
|
|
return request.user.is_superuser or obj is not None |
48
|
|
|
|
49
|
|
|
# pylint: disable=too-many-arguments |
50
|
|
|
def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None): |
51
|
|
|
if obj and not (_modifying_myself(request, obj.pk) or request.user.is_superuser): |
52
|
|
|
context.update({ |
53
|
|
|
'show_save': False, |
54
|
|
|
'show_save_and_continue': False, |
55
|
|
|
}) |
56
|
|
|
return super().render_change_form(request, |
57
|
|
|
context, |
58
|
|
|
add=add, |
59
|
|
|
change=change, |
60
|
|
|
form_url=form_url, |
61
|
|
|
obj=obj) |
62
|
|
|
|
63
|
|
|
def get_readonly_fields(self, request, obj=None): |
64
|
|
|
if request.user.is_superuser: |
65
|
|
|
return super().get_readonly_fields(request, obj) |
66
|
|
|
|
67
|
|
|
readonly_fields = ['username', 'is_staff', 'is_active', |
68
|
|
|
'is_superuser', 'last_login', 'date_joined', |
69
|
|
|
'groups', 'user_permissions'] |
70
|
|
|
if obj and not _modifying_myself(request, obj.pk): |
71
|
|
|
readonly_fields.extend(['first_name', 'last_name', 'email']) |
72
|
|
|
|
73
|
|
|
return readonly_fields |
74
|
|
|
|
75
|
|
|
def get_fieldsets(self, request, obj=None): |
76
|
|
|
# super-user adding new account |
77
|
|
|
if not obj and request.user.is_superuser: |
78
|
|
|
return super().get_fieldsets(request, obj) |
79
|
|
|
|
80
|
|
|
first_fieldset_fields = ('username',) |
81
|
|
|
if obj and _modifying_myself(request, obj.pk): |
82
|
|
|
first_fieldset_fields = first_fieldset_fields + ('password',) |
83
|
|
|
|
84
|
|
|
remaining_fieldsets = ( |
85
|
|
|
(_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}), |
86
|
|
|
(_('Permissions'), {'fields': ('is_active', 'groups')}), |
87
|
|
|
) |
88
|
|
|
|
89
|
|
|
if request.user.is_superuser: |
90
|
|
|
field_sets = super().get_fieldsets(request, obj) |
91
|
|
|
if field_sets[0][0] is None and 'password' in field_sets[0][1]['fields']: |
92
|
|
|
remaining_fieldsets = field_sets[1:] |
93
|
|
|
|
94
|
|
|
return ((None, {'fields': first_fieldset_fields}),) + remaining_fieldsets |
95
|
|
|
|
96
|
|
|
@sensitive_post_parameters_m |
97
|
|
|
def user_change_password(self, request, id, form_url=''): # pylint: disable=redefined-builtin |
98
|
|
|
return HttpResponseRedirect(reverse('admin:password_change')) |
99
|
|
|
|
100
|
|
|
@admin.options.csrf_protect_m |
101
|
|
|
def delete_view(self, request, object_id, extra_context=None): |
102
|
|
|
if not _modifying_myself(request, object_id): |
103
|
|
|
return super().delete_view(request, object_id, extra_context) |
104
|
|
|
|
105
|
|
|
# allow deletion of the user own account |
106
|
|
|
permission = Permission.objects.get(content_type__app_label='auth', |
107
|
|
|
codename='delete_user') |
108
|
|
|
try: |
109
|
|
|
request.user.user_permissions.add(permission) |
110
|
|
|
return super().delete_view(request, object_id, extra_context) |
111
|
|
|
finally: |
112
|
|
|
request.user.user_permissions.remove(permission) |
113
|
|
|
|
114
|
|
|
def response_delete(self, request, obj_display, obj_id): |
115
|
|
|
result = super().response_delete(request, obj_display, obj_id) |
116
|
|
|
|
117
|
|
|
if not _modifying_myself(request, obj_id): |
118
|
|
|
return result |
119
|
|
|
|
120
|
|
|
# user doesn't exist anymore so go to the index page |
121
|
|
|
# instead of returning to the user admin page |
122
|
|
|
return HttpResponseRedirect(reverse('core-views-index')) |
123
|
|
|
|
124
|
|
|
def has_delete_permission(self, request, obj=None): |
125
|
|
|
# allow to delete yourself without having 'delete' permission |
126
|
|
|
# explicitly assigned |
127
|
|
|
if _modifying_myself(request, getattr(obj, 'pk', 0)): |
128
|
|
|
return True |
129
|
|
|
|
130
|
|
|
return super().has_delete_permission(request, obj) |
131
|
|
|
|
132
|
|
|
def delete_model(self, request, obj): |
133
|
|
|
delete_user(obj) |
134
|
|
|
|
135
|
|
|
|
136
|
|
|
# user admin extended functionality |
137
|
|
|
admin.site.unregister(User) |
138
|
|
|
admin.site.register(User, KiwiUserAdmin) |
139
|
|
|
|