Explorar o código

Merge pull request #23284 from netbox-community/23281-changing-a-new-users-preferences-can-modify-the-defaults-for

Fixes #23281: Prevent changes to new Users' preferences from modifying shared defaults
bctiemann hai 1 día
pai
achega
45ed48ee93

+ 1 - 3
netbox/core/tests/test_views.py

@@ -194,9 +194,7 @@ class JobLogViewTestCase(TestCase):
 
 
     def setUp(self):
     def setUp(self):
         super().setUp()
         super().setUp()
-        # UserConfig.set() mutates self.data in place, which can mutate DEFAULT_USER_PREFERENCES
-        # (the signal in users/signals.py initializes data with a shared reference). Assign a
-        # fresh literal instead. Pin per_page so page-boundary assertions don't depend on PAGINATE_COUNT.
+        # Pin per_page so page-boundary assertions don't depend on PAGINATE_COUNT.
         self.user.config.data = {'pagination': {'per_page': 50}}
         self.user.config.data = {'pagination': {'per_page': 50}}
         self.user.config.save()
         self.user.config.save()
 
 

+ 2 - 1
netbox/users/signals.py

@@ -1,3 +1,4 @@
+import copy
 import logging
 import logging
 
 
 from django.contrib.auth.signals import user_logged_in, user_login_failed
 from django.contrib.auth.signals import user_logged_in, user_login_failed
@@ -42,4 +43,4 @@ def create_userconfig(instance, created, raw=False, **kwargs):
     """
     """
     if created and not raw:
     if created and not raw:
         config = get_config()
         config = get_config()
-        UserConfig(user=instance, data=config.DEFAULT_USER_PREFERENCES).save()
+        UserConfig(user=instance, data=copy.deepcopy(config.DEFAULT_USER_PREFERENCES)).save()

+ 16 - 2
netbox/users/tests/test_signals.py

@@ -1,7 +1,8 @@
 from django.contrib.auth.signals import user_logged_in, user_login_failed
 from django.contrib.auth.signals import user_logged_in, user_login_failed
 from django.db.models.signals import post_save
 from django.db.models.signals import post_save
-from django.test import RequestFactory, TestCase, override_settings
+from django.test import RequestFactory, TestCase, override_settings, tag
 
 
+from netbox.config import get_config
 from users.models import User, UserConfig
 from users.models import User, UserConfig
 from users.signals import create_userconfig
 from users.signals import create_userconfig
 
 
@@ -60,7 +61,6 @@ class SetLanguageOnLoginSignalTestCase(TestCase):
         self.user = User.objects.create_user(username='alice', password='pw')
         self.user = User.objects.create_user(username='alice', password='pw')
 
 
     def test_language_cookie_is_set_from_user_config(self):
     def test_language_cookie_is_set_from_user_config(self):
-        # Assign a fresh dict to avoid mutating the shared DEFAULT_USER_PREFERENCES reference.
         self.user.config.data = {'locale': {'language': 'de'}}
         self.user.config.data = {'locale': {'language': 'de'}}
         self.user.config.save()
         self.user.config.save()
         request = self.factory.post('/login/')
         request = self.factory.post('/login/')
@@ -102,6 +102,20 @@ class CreateUserConfigSignalTestCase(TestCase):
         config = UserConfig.objects.get(user=user)
         config = UserConfig.objects.get(user=user)
         self.assertEqual(config.data, {'pagination.per_page': 42})
         self.assertEqual(config.data, {'pagination.per_page': 42})
 
 
+    @tag('regression')  # Ref: #23281
+    @override_settings(DEFAULT_USER_PREFERENCES={'locale': {'language': 'de'}, 'pagination': {'per_page': 25}})
+    def test_userconfig_changes_do_not_alter_default_preferences(self):
+        """Setting or clearing a new user's preferences changes neither the defaults nor later users."""
+        user = User.objects.create_user(username='alice', password='pw')
+        user.config.set('pagination.per_page', 50)
+        user.config.clear('locale.language')
+
+        later_user = User.objects.create_user(username='bob', password='pw')
+
+        defaults = {'locale': {'language': 'de'}, 'pagination': {'per_page': 25}}
+        self.assertEqual(get_config().DEFAULT_USER_PREFERENCES, defaults)
+        self.assertEqual(UserConfig.objects.get(user=later_user).data, defaults)
+
     def test_userconfig_is_not_created_for_existing_user(self):
     def test_userconfig_is_not_created_for_existing_user(self):
         user = User.objects.create_user(username='alice', password='pw')
         user = User.objects.create_user(username='alice', password='pw')
         UserConfig.objects.filter(user=user).delete()
         UserConfig.objects.filter(user=user).delete()