From 65a055c5223fbc889b7625b8fb698349d5cc5fef Mon Sep 17 00:00:00 2001 From: quimmrc Date: Fri, 13 Dec 2024 12:12:06 +0100 Subject: [PATCH 1/5] profile update: allow spaces for existing usernames (but also for modified ones) --- accounts/forms.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/accounts/forms.py b/accounts/forms.py index 61ff350ae..c47bc61fe 100644 --- a/accounts/forms.py +++ b/accounts/forms.py @@ -153,11 +153,17 @@ def get_user_by_email(email): class UsernameField(forms.CharField): """ Username field, 3~30 characters, allows only alphanumeric chars, required by default """ def __init__(self, required=True): + #NOTE: this allows space characters for both pre-existing usernames (OK) + # but also for modified new ones (not OK). It does not allow them for brand new ones (OK) + if required: + validators = [RegexValidator(r'^[\w.+-]+$')] # is the same as Django UsernameValidator except for '@' symbol + else: + validators = [RegexValidator(r'^[\w .+-]+$')] #same as the last one but with space characters included super().__init__( label="Username", min_length=3, max_length=30, - validators=[RegexValidator(r'^[\w.+-]+$')], # is the same as Django UsernameValidator except for '@' symbol + validators=validators, help_text="30 characters or fewer. Can contain: letters, digits, underscores, dots, dashes and plus signs.", error_messages={'invalid': "The username field must contain only letters, digits, underscores, dots, dashes and " "plus signs."}, @@ -391,6 +397,7 @@ def clean_username(self): # If username was not changed, consider it valid if username.lower() == self.request.user.username.lower(): + #self.fields["username"] = UsernameField(validation=False) return username # Check that username is not used by another user. Note that because when the maximum number of username From 6a51d80d32a33edab6c66bbc10dd298f0dcb134b Mon Sep 17 00:00:00 2001 From: quimmrc Date: Fri, 13 Dec 2024 13:04:20 +0100 Subject: [PATCH 2/5] enable profile updates for antique usernames containing spaces --- accounts/forms.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/accounts/forms.py b/accounts/forms.py index c47bc61fe..1285ae36e 100644 --- a/accounts/forms.py +++ b/accounts/forms.py @@ -152,13 +152,19 @@ def get_user_by_email(email): class UsernameField(forms.CharField): """ Username field, 3~30 characters, allows only alphanumeric chars, required by default """ + def __init__(self, required=True): - #NOTE: this allows space characters for both pre-existing usernames (OK) - # but also for modified new ones (not OK). It does not allow them for brand new ones (OK) + """Validates the username field for a form. Validation for brand new usernames must have strong validation (see Regex). + For profile modifications, the username validation is done in ProfileForm cleaning methods, as antique usernames can + contain spaces but new modified ones cannot. + + Args: + required (bool, optional): True for RegistrationForms, false for ProfileForms + """ if required: validators = [RegexValidator(r'^[\w.+-]+$')] # is the same as Django UsernameValidator except for '@' symbol else: - validators = [RegexValidator(r'^[\w .+-]+$')] #same as the last one but with space characters included + validators = [] super().__init__( label="Username", min_length=3, @@ -395,10 +401,16 @@ def clean_username(self): if not username: username = self.request.user.username - # If username was not changed, consider it valid + # If username was not changed, consider it valid. If it has, validate it to check it does not contain space characters. if username.lower() == self.request.user.username.lower(): - #self.fields["username"] = UsernameField(validation=False) return username + else: + validator = RegexValidator(regex=r'^[\w.+-]+$', + message="The username field must contain only letters, digits, underscores, dots, dashes and plus signs.", + code='invalid') + if validator(username): + return username + # Check that username is not used by another user. Note that because when the maximum number of username # changes is reached, the "username" field of the ProfileForm is disabled and its contents won't change. From 4cb2b9aca02f69ddb755b6c1962647ad7283af88 Mon Sep 17 00:00:00 2001 From: quimmrc Date: Tue, 21 Jan 2025 19:25:52 +0100 Subject: [PATCH 3/5] add unit test for username whitespace validation --- accounts/tests/test_user.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/accounts/tests/test_user.py b/accounts/tests/test_user.py index 322b6a08b..7f781830f 100644 --- a/accounts/tests/test_user.py +++ b/accounts/tests/test_user.py @@ -1085,7 +1085,30 @@ def test_oldusername_username_unique_case_insensitiveness(self): OldUsername.objects.create(user=userA, username='newUserAUsername') with self.assertRaises(IntegrityError): OldUsername.objects.create(user=userA, username='NewUserAUsername') + + def test_username_whitespace(self): + """Test that for usernames created before stronger validation was applied, whitespaces are a valid character + but for new edited ones they are not.""" + userA = User.objects.create_user('user A', email='userA@freesound.org', password='testpass') + self.client.login(username='user A', password='testpass') + + # Test save profile without changing username with whitespaces + resp = self.client.post(reverse('accounts-edit'), data={'profile-username': ['user A'], 'profile-ui_theme_preference': 'f'}) + self.assertRedirects(resp, reverse('accounts-edit')) + self.assertEqual(OldUsername.objects.filter(user=userA).count(), 0) + # Test save profile changing username (no whitespaces) + resp = self.client.post(reverse('accounts-edit'), data={'profile-username': ['userANewName'], 'profile-ui_theme_preference': 'f'}) + self.assertRedirects(resp, reverse('accounts-edit')) + userA.refresh_from_db() + self.assertEqual(OldUsername.objects.filter(user=userA).count(), 1) + + # Test save profile changing username (whitespaces -> fail) + resp = self.client.post(reverse('accounts-edit'), data={'profile-username': ['userA SpaceName'], 'profile-ui_theme_preference': 'f'}) + self.assertEqual(resp.status_code, 200) + userA.refresh_from_db() + self.assertEqual(userA.username, 'userANewName') + self.assertEqual(OldUsername.objects.filter(user=userA).count(), 1) class ChangeEmailViaAdminTestCase(TestCase): From 8c7369ce56b3225f8b3255d89336d504cd6f56db Mon Sep 17 00:00:00 2001 From: quimmrc Date: Thu, 23 Jan 2025 10:07:21 +0100 Subject: [PATCH 4/5] graylog error solving --- monitor/tests.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/monitor/tests.py b/monitor/tests.py index 3723d9bcd..c8cf58a0a 100644 --- a/monitor/tests.py +++ b/monitor/tests.py @@ -17,7 +17,7 @@ def test_monitor_queries_stats_ajax_error(self, mock_get): resp = self.client.get(reverse('monitor-queries-stats-ajax')) self.assertEqual(resp.status_code, 500) - mock_get.assert_called_with('http://graylog/graylog/api/search/universal/relative/terms', auth=mock.ANY, params=mock.ANY) + mock_get.assert_called_with('http://graylog/api/search/universal/relative/terms', auth=mock.ANY, params=mock.ANY) @override_settings(GRAYLOG_DOMAIN='http://graylog') @mock.patch('requests.get') @@ -29,7 +29,7 @@ def test_monitor_queries_stats_ajax_bad_data(self, mock_get): resp = self.client.get(reverse('monitor-queries-stats-ajax')) self.assertEqual(resp.status_code, 500) - mock_get.assert_called_with('http://graylog/graylog/api/search/universal/relative/terms', auth=mock.ANY, params=mock.ANY) + mock_get.assert_called_with('http://graylog/api/search/universal/relative/terms', auth=mock.ANY, params=mock.ANY) @override_settings(GRAYLOG_DOMAIN='http://graylog') @mock.patch('requests.get') @@ -43,4 +43,4 @@ def test_monitor_queries_stats_ajax_ok(self, mock_get): self.assertEqual(resp.status_code, 200) self.assertJSONEqual(resp.content, {'response': 'ok'}) - mock_get.assert_called_with('http://graylog/graylog/api/search/universal/relative/terms', auth=mock.ANY, params=mock.ANY) + mock_get.assert_called_with('http://graylog/api/search/universal/relative/terms', auth=mock.ANY, params=mock.ANY) From 94dd2ded5e26547a76e04e68a858d23e04dcd6aa Mon Sep 17 00:00:00 2001 From: ffont Date: Tue, 11 Feb 2025 11:46:35 +0100 Subject: [PATCH 5/5] Use force_login --- accounts/tests/test_user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accounts/tests/test_user.py b/accounts/tests/test_user.py index ad6f64171..3be72b7f3 100644 --- a/accounts/tests/test_user.py +++ b/accounts/tests/test_user.py @@ -1090,7 +1090,7 @@ def test_username_whitespace(self): """Test that for usernames created before stronger validation was applied, whitespaces are a valid character but for new edited ones they are not.""" userA = User.objects.create_user('user A', email='userA@freesound.org', password='testpass') - self.client.login(username='user A', password='testpass') + self.client.force_login(userA) # Test save profile without changing username with whitespaces resp = self.client.post(reverse('accounts-edit'), data={'profile-username': ['user A'], 'profile-ui_theme_preference': 'f'})