|
1 | 1 | """ |
2 | 2 | Admin site configuration for third party authentication |
3 | 3 | """ |
4 | | - |
| 4 | +import csv |
5 | 5 |
|
6 | 6 | from config_models.admin import KeyedConfigurationModelAdmin |
7 | 7 | from django import forms |
8 | | -from django.contrib import admin |
| 8 | +from django.contrib import admin, messages |
9 | 9 | from django.db import transaction |
10 | | -from django.urls import reverse |
| 10 | +from django.http import Http404, HttpResponseRedirect |
| 11 | +from django.urls import path, reverse |
11 | 12 | from django.utils.html import format_html |
12 | 13 | from django.utils.translation import gettext_lazy as _ |
| 14 | +from django.views.decorators.csrf import csrf_exempt |
13 | 15 |
|
14 | 16 | from .models import ( |
15 | 17 | _PSA_OAUTH2_BACKENDS, |
|
21 | 23 | SAMLProviderConfig, |
22 | 24 | SAMLProviderData |
23 | 25 | ) |
24 | | -from .tasks import fetch_saml_metadata |
| 26 | +from .tasks import fetch_saml_metadata, update_saml_users_social_auth_uid |
25 | 27 |
|
26 | 28 |
|
27 | 29 | class OAuth2ProviderConfigForm(forms.ModelForm): |
@@ -72,7 +74,7 @@ def get_list_display(self, request): |
72 | 74 | """ Don't show every single field in the admin change list """ |
73 | 75 | return ( |
74 | 76 | 'name_with_update_link', 'enabled', 'site', 'entity_id', 'metadata_source', |
75 | | - 'has_data', 'mode', 'saml_configuration', 'change_date', 'changed_by', |
| 77 | + 'has_data', 'mode', 'saml_configuration', 'change_date', 'changed_by', 'csv_uuid_update_button', |
76 | 78 | ) |
77 | 79 |
|
78 | 80 | list_display_links = None |
@@ -135,6 +137,65 @@ def save_model(self, request, obj, form, change): |
135 | 137 | super().save_model(request, obj, form, change) |
136 | 138 | fetch_saml_metadata.apply_async((), countdown=2) |
137 | 139 |
|
| 140 | + def get_urls(self): |
| 141 | + """ Extend the admin URLs to include the custom CSV upload URL. """ |
| 142 | + urls = super().get_urls() |
| 143 | + custom_urls = [ |
| 144 | + path('<slug:slug>/upload-csv/', self.admin_site.admin_view(self.upload_csv), name='upload_csv'), |
| 145 | + |
| 146 | + ] |
| 147 | + return custom_urls + urls |
| 148 | + |
| 149 | + @csrf_exempt |
| 150 | + def upload_csv(self, request, slug): |
| 151 | + """ Handle CSV upload and update UserSocialAuth model. """ |
| 152 | + if not request.user.is_staff: |
| 153 | + raise Http404 |
| 154 | + if request.method == 'POST': |
| 155 | + csv_file = request.FILES.get('csv_file') |
| 156 | + if not csv_file or not csv_file.name.endswith('.csv'): |
| 157 | + self.message_user(request, "Please upload a valid CSV file.", level=messages.ERROR) |
| 158 | + else: |
| 159 | + try: |
| 160 | + decoded_file = csv_file.read().decode('utf-8').splitlines() |
| 161 | + reader = csv.DictReader(decoded_file) |
| 162 | + update_saml_users_social_auth_uid(reader, slug) |
| 163 | + self.message_user(request, "CSV file has been processed successfully.") |
| 164 | + except Exception as e: # pylint: disable=broad-except |
| 165 | + self.message_user(request, f"Failed to process CSV file: {e}", level=messages.ERROR) |
| 166 | + |
| 167 | + # Always redirect back to the SAMLProviderConfig listing page |
| 168 | + return HttpResponseRedirect(reverse('admin:third_party_auth_samlproviderconfig_changelist')) |
| 169 | + |
| 170 | + def change_view(self, request, object_slug, form_url='', extra_context=None): |
| 171 | + """ Extend the change view to include CSV upload. """ |
| 172 | + extra_context = extra_context or {} |
| 173 | + extra_context['show_csv_upload'] = True |
| 174 | + return super().change_view(request, object_slug, form_url, extra_context) |
| 175 | + |
| 176 | + def csv_uuid_update_button(self, obj): |
| 177 | + """ Add CSV upload button to the form. """ |
| 178 | + if obj: |
| 179 | + form_url = reverse('admin:upload_csv', args=[obj.slug]) |
| 180 | + return format_html( |
| 181 | + '<form method="post" enctype="multipart/form-data" action="{}">' |
| 182 | + '<input type="file" name="csv_file" accept=".csv" style="margin-bottom: 10px;">' |
| 183 | + '<button type="submit" class="button">Upload CSV</button>' |
| 184 | + '</form>', |
| 185 | + form_url |
| 186 | + ) |
| 187 | + return "" |
| 188 | + |
| 189 | + csv_uuid_update_button.short_description = 'UUID UPDATE CSV' |
| 190 | + csv_uuid_update_button.allow_tags = True |
| 191 | + |
| 192 | + def get_readonly_fields(self, request, obj=None): |
| 193 | + """ Conditionally add csv_uuid_update_button to readonly fields. """ |
| 194 | + readonly_fields = list(super().get_readonly_fields(request, obj)) |
| 195 | + if obj: |
| 196 | + readonly_fields.append('csv_uuid_update_button') |
| 197 | + return readonly_fields |
| 198 | + |
138 | 199 | admin.site.register(SAMLProviderConfig, SAMLProviderConfigAdmin) |
139 | 200 |
|
140 | 201 |
|
|
0 commit comments