From 3c23ca0fd62bc527562ba4ee13fee4416ff4410e Mon Sep 17 00:00:00 2001 From: Fabian Geisberger Date: Mon, 8 Jun 2026 10:49:32 -0400 Subject: [PATCH 1/3] Fixes #22397: Don't get user configs for unauthenticated users during bulk export --- netbox/netbox/views/generic/bulk_views.py | 4 ++-- netbox/utilities/testing/views.py | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/netbox/netbox/views/generic/bulk_views.py b/netbox/netbox/views/generic/bulk_views.py index 65e2a58aee9..eef9e490b9e 100644 --- a/netbox/netbox/views/generic/bulk_views.py +++ b/netbox/netbox/views/generic/bulk_views.py @@ -181,7 +181,7 @@ def get(self, request): if request.GET['export'] == 'table': table = self.get_table(self.queryset, request, has_table_actions) columns = [name for name, _ in table.selected_columns] - delimiter = request.user.config.get('csv_delimiter') + delimiter = request.user.config.get('csv_delimiter') if request.user.is_authenticated else None return self.export_table(table, columns, delimiter=delimiter) # Render an ExportTemplate @@ -202,7 +202,7 @@ def get(self, request): # Fall back to default table/YAML export table = self.get_table(self.queryset, request, has_table_actions) - delimiter = request.user.config.get('csv_delimiter') + delimiter = request.user.config.get('csv_delimiter') if request.user.is_authenticated else None return self.export_table(table, delimiter=delimiter) # Render the objects table diff --git a/netbox/utilities/testing/views.py b/netbox/utilities/testing/views.py index ac24c61d525..fb060349d34 100644 --- a/netbox/utilities/testing/views.py +++ b/netbox/utilities/testing/views.py @@ -517,6 +517,21 @@ def test_export_objects(self): self.assertHttpStatus(response, 200) self.assertEqual(response.get('Content-Type'), 'text/csv; charset=utf-8') + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], LOGIN_REQUIRED=False) + def test_export_objects_anonymous(self): + self.client.logout() + url = self._get_url('list') + + # Test default CSV export + response = self.client.get(f'{url}?export') + self.assertHttpStatus(response, 200) + self.assertEqual(response.get('Content-Type'), 'text/csv; charset=utf-8') + + # Test table-based export + response = self.client.get(f'{url}?export=table') + self.assertHttpStatus(response, 200) + self.assertEqual(response.get('Content-Type'), 'text/csv; charset=utf-8') + class CreateMultipleObjectsViewTestCase(ModelViewTestCase): """ Create multiple instances using a single form. Expects the creation of three new instances by default. From b9c76dd076755cad218b23cc944dea69058751f5 Mon Sep 17 00:00:00 2001 From: Fabian Geisberger Date: Mon, 8 Jun 2026 21:39:17 -0400 Subject: [PATCH 2/3] fix tests --- netbox/extras/tests/test_views.py | 9 +++++++++ netbox/users/tests/query_counts.json | 2 +- netbox/utilities/testing/views.py | 14 ++++++++++++-- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/netbox/extras/tests/test_views.py b/netbox/extras/tests/test_views.py index f97b3581bd6..8dd82c239b1 100644 --- a/netbox/extras/tests/test_views.py +++ b/netbox/extras/tests/test_views.py @@ -408,6 +408,9 @@ def _get_url(self, action, instance=None): def test_list_objects_anonymous(self): return + def test_export_objects_anonymous(self): + return + def test_list_objects_with_constrained_permission(self): return @@ -962,6 +965,9 @@ def test_list_objects_anonymous(self): login_url = reverse('login') self.assertRedirects(self.client.get(url), f'{login_url}?next={url}') + def test_export_objects_anonymous(self): + return + def test_list_objects_with_permission(self): return @@ -1070,6 +1076,9 @@ def test_list_objects_anonymous(self): login_url = reverse('login') self.assertRedirects(self.client.get(url), f'{login_url}?next={url}') + def test_export_objects_anonymous(self): + return + def test_list_objects_with_permission(self): return diff --git a/netbox/users/tests/query_counts.json b/netbox/users/tests/query_counts.json index 697e0c65c42..83643dc32fe 100644 --- a/netbox/users/tests/query_counts.json +++ b/netbox/users/tests/query_counts.json @@ -9,6 +9,6 @@ "ownergroup:list_objects_with_permission": 17, "token:api_list_objects": 10, "token:list_objects_with_permission": 17, - "user:api_list_objects": 12, + "user:api_list_objects": 11, "user:list_objects_with_permission": 17 } diff --git a/netbox/utilities/testing/views.py b/netbox/utilities/testing/views.py index fb060349d34..33c9ac25dd7 100644 --- a/netbox/utilities/testing/views.py +++ b/netbox/utilities/testing/views.py @@ -519,13 +519,23 @@ def test_export_objects(self): @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], LOGIN_REQUIRED=False) def test_export_objects_anonymous(self): + # Ensure we are logged out. self.client.logout() + + # Some models (e.g. the users model) always require to be logged in, so we skip them here. + ct = ContentType.objects.get_for_model(self.model) + if (ct.app_label, ct.model) in settings.EXEMPT_EXCLUDE_MODELS: + return + url = self._get_url('list') - # Test default CSV export + # Test default CSV (or sometimes YAML) export response = self.client.get(f'{url}?export') self.assertHttpStatus(response, 200) - self.assertEqual(response.get('Content-Type'), 'text/csv; charset=utf-8') + if hasattr(self.model, 'to_yaml'): + self.assertEqual(response.get('Content-Type'), 'text/yaml') + else: + self.assertEqual(response.get('Content-Type'), 'text/csv; charset=utf-8') # Test table-based export response = self.client.get(f'{url}?export=table') From 42703c4b0a11581b67351701914bee4c863d42d4 Mon Sep 17 00:00:00 2001 From: Fabian Geisberger Date: Tue, 9 Jun 2026 11:59:00 -0400 Subject: [PATCH 3/3] revert count --- netbox/users/tests/query_counts.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/users/tests/query_counts.json b/netbox/users/tests/query_counts.json index 83643dc32fe..697e0c65c42 100644 --- a/netbox/users/tests/query_counts.json +++ b/netbox/users/tests/query_counts.json @@ -9,6 +9,6 @@ "ownergroup:list_objects_with_permission": 17, "token:api_list_objects": 10, "token:list_objects_with_permission": 17, - "user:api_list_objects": 11, + "user:api_list_objects": 12, "user:list_objects_with_permission": 17 }