Coverage for baibu/users/admin.py: 98%
84 statements
« prev ^ index » next coverage.py v7.10.7, created at 2026-10-06 13:07 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2026-10-06 13:07 +0000
1from allauth.account.decorators import secure_admin_login
2from django import forms
3from django.conf import settings
4from django.contrib import admin
5from django.contrib.auth import admin as auth_admin
6from django.utils import timezone
7from django.utils.translation import gettext_lazy as _
9from .forms import UserAdminChangeForm
10from .forms import UserAdminCreationForm
11from .models import AccountDeletionRequest
12from .models import ConsentRecord
13from .models import User
15if settings.DJANGO_ADMIN_FORCE_ALLAUTH:
16 admin.autodiscover()
17 admin.site.login = secure_admin_login(admin.site.login) # type: ignore[method-assign]
20class ConsentRecordInline(admin.TabularInline):
21 model = ConsentRecord
22 extra = 0
23 can_delete = False
24 fields = ("scope", "tier", "source", "granted_at", "revoked_at")
25 readonly_fields = fields
27 def has_add_permission(self, request, obj=None):
28 return False
31@admin.register(User)
32class UserAdmin(auth_admin.UserAdmin):
33 form = UserAdminChangeForm
34 add_form = UserAdminCreationForm
35 inlines = (ConsentRecordInline,)
36 fieldsets = (
37 (None, {"fields": ("email", "phone", "phone_verified", "password")}),
38 (_("Profile"), {"fields": ("name", "city", "country")}),
39 (
40 _("Permissions"),
41 {"fields": ("is_active", "is_staff", "is_superuser", "groups", "user_permissions")},
42 ),
43 (_("Important dates"), {"fields": ("last_login", "date_joined")}),
44 )
45 add_fieldsets = ((None, {"classes": ("wide",), "fields": ("email", "password1", "password2")}),)
46 list_display = ("email", "name", "is_active", "is_staff", "date_joined")
47 list_filter = ("is_active", "is_staff", "is_superuser")
48 search_fields = ("email", "name", "phone")
49 ordering = ("-date_joined",)
51 def get_inlines(self, request, obj):
52 # Consent history only exists once the user does.
53 return self.inlines if obj else ()
56@admin.register(ConsentRecord)
57class ConsentRecordAdmin(admin.ModelAdmin):
58 """Read-only: consent history is append-only."""
60 list_display = ("user", "scope", "tier", "source", "granted_at", "revoked_at")
61 list_filter = ("scope", "tier", "source")
62 search_fields = ("user__email", "user__name")
63 readonly_fields = ("id", "user", "tier", "source", "scope", "granted_at", "revoked_at", "metadata", "created_at")
65 def has_add_permission(self, request):
66 return False
68 def has_change_permission(self, request, obj=None):
69 return False
71 def has_delete_permission(self, request, obj=None):
72 return False
75class AccountDeletionRequestAdminForm(forms.ModelForm):
76 class Meta:
77 model = AccountDeletionRequest
78 fields = ("status", "resolution_notes")
80 def clean_status(self):
81 new_status = self.cleaned_data["status"]
82 current = AccountDeletionRequest.objects.get(pk=self.instance.pk)
83 if not current.can_transition_to(new_status):
84 raise forms.ValidationError(
85 _("A request cannot move from %(old)s to %(new)s."),
86 params={"old": current.get_status_display(), "new": new_status},
87 )
88 return new_status
91@admin.register(AccountDeletionRequest)
92class AccountDeletionRequestAdmin(admin.ModelAdmin):
93 form = AccountDeletionRequestAdminForm
94 list_display = ("id", "status", "source", "requester_email", "requested_at", "completed_at")
95 list_filter = ("status", "source")
96 search_fields = ("id", "requester_email", "requester_name", "user__email")
97 readonly_fields = (
98 "id",
99 "source",
100 "user",
101 "requester_name",
102 "requester_email",
103 "request_details",
104 "metadata",
105 "requested_at",
106 "updated_at",
107 "completed_at",
108 )
109 fields = (*readonly_fields, "status", "resolution_notes")
110 actions = ("mark_completed",)
112 def has_add_permission(self, request):
113 return False
115 def save_model(self, request, obj, form, change):
116 if obj.status == AccountDeletionRequest.Status.COMPLETED and obj.completed_at is None:
117 obj.completed_at = timezone.now()
118 obj.clear_personal_details()
119 super().save_model(request, obj, form, change)
121 @admin.action(description=_("Mark selected in-progress requests as completed"))
122 def mark_completed(self, request, queryset):
123 updated = 0
124 for deletion_request in queryset.filter(status=AccountDeletionRequest.Status.IN_PROGRESS):
125 deletion_request.status = AccountDeletionRequest.Status.COMPLETED
126 deletion_request.completed_at = timezone.now()
127 deletion_request.clear_personal_details()
128 deletion_request.save()
129 updated += 1
130 self.message_user(request, _("%(count)d request(s) marked completed.") % {"count": updated})