From 33292ed950da51e311c03bd42bdc105b9ae031dc Mon Sep 17 00:00:00 2001 From: FrankApiyo Date: Wed, 18 Sep 2024 18:53:39 +0300 Subject: [PATCH] Update rules for setting usernames from emails If an email has a valid username we will use it to set the username --- README.md | 2 +- oidc/settings.py | 2 +- oidc/viewsets.py | 3 +- tests/test_viewsets.py | 74 +++++++++++++++++++++++++++++++++++++++++- 4 files changed, 77 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 809323c..6454932 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ OPENID_CONNECT_VIEWSET_CONFIG = { # that's used to validate all field inputs retrieved for the particular key "FIELD_VALIDATION_REGEX": { "username": { - "regex": "^(?!\d+$).{4,}$", + "regex": "^(?!\d+$)[a-zA-Z0-9]{3,}$", "help_text": "Username should only contain alpha numeric characters", } }, diff --git a/oidc/settings.py b/oidc/settings.py index c8a3477..c5d9076 100644 --- a/oidc/settings.py +++ b/oidc/settings.py @@ -18,7 +18,7 @@ "JWT_ALGORITHM": "HS256", "FIELD_VALIDATION_REGEX": { "username": { - "regex": "^(?!\d+$).{4,}$", # noqa + "regex": "^(?!\d+$)[a-zA-Z0-9_]{3,}$", # noqa "help_text": "Username should only contain alpha numeric characters", } }, diff --git a/oidc/viewsets.py b/oidc/viewsets.py index 25faedc..63a1bbc 100644 --- a/oidc/viewsets.py +++ b/oidc/viewsets.py @@ -257,7 +257,8 @@ def _clean_user_data(self, user_data) -> Tuple[dict, Optional[list]]: and username_regex.search(username) ): user_data["username"] = username - missing_fields.remove("username") + if "username" in missing_fields: + missing_fields.remove("username") return user_data, missing_fields diff --git a/tests/test_viewsets.py b/tests/test_viewsets.py index b18e15e..154f9e7 100644 --- a/tests/test_viewsets.py +++ b/tests/test_viewsets.py @@ -45,9 +45,10 @@ "SSO_COOKIE_DATA": "email", "JWT_ALGORITHM": "HS256", "JWT_SECRET_KEY": "abc", + "REPLACE_USERNAME_CHARACTERS": "-.", "FIELD_VALIDATION_REGEX": { "username": { - "regex": "^(?!\d+$).{4,}$", + "regex": "^(?!\d+$)[a-zA-Z0-9_]{3,}$", "help_text": "Username should only contain word characters & numbers i.e datatester23", }, }, @@ -84,6 +85,77 @@ def test_returns_data_entry_template_on_missing_username_claim(self): self.assertEqual(response.status_code, 200) self.assertEqual(response.template_name, "oidc/oidc_user_data_entry.html") + @override_settings(OPENID_CONNECT_VIEWSET_CONFIG=OPENID_CONNECT_VIEWSET_CONFIG) + def test_user_created_successfully_when_email_has_a_valid_username(self): + """ + Test that the user is created ok when + username is not present in decoded token but email has a valid username + """ + view = UserModelOpenIDConnectViewset.as_view({"post": "callback"}) + with patch( + "oidc.viewsets.OpenIDClient.verify_and_decode_id_token" + ) as mock_func: + mock_func.return_value = { + "family_name": "bob", + "given_name": "just bob", + "username": "boby@example.com", + "email": "boby@example.com", + } + + data = {"id_token": "sadsdaio3209lkasdlkas0d.sdojdsiad.iosdadia"} + request = self.factory.post("/", data=data) + response = view(request, auth_server="default") + self.assertEqual(response.status_code, 302) + user = User.objects.get(username="boby") + self.assertEqual(user.email, "boby@example.com") + + @override_settings(OPENID_CONNECT_VIEWSET_CONFIG=OPENID_CONNECT_VIEWSET_CONFIG) + def test_returns_data_entry_template_on_invalid_username(self): + """ + Test that users are redirected to the data entry + page when username is not present in decoded token and + provided email also does not provide a valid username + """ + view = UserModelOpenIDConnectViewset.as_view({"post": "callback"}) + with patch( + "oidc.viewsets.OpenIDClient.verify_and_decode_id_token" + ) as mock_func: + mock_func.return_value = { + "family_name": "bob", + "given_name": "just bob", + "email": "bo@example.com", + } + + data = {"id_token": "sadsdaio3209lkasdlkas0d.sdojdsiad.iosdadia"} + request = self.factory.post("/", data=data) + response = view(request, auth_server="default") + self.assertEqual(response.status_code, 200) + self.assertEqual(response.template_name, "oidc/oidc_user_data_entry.html") + + @override_settings(OPENID_CONNECT_VIEWSET_CONFIG=OPENID_CONNECT_VIEWSET_CONFIG) + def test_returns_data_entry_template_on_invalid_username_and_bad_email(self): + """ + Test that users are redirected to the data entry + page when username is not present in decoded token and + provided email also does not provide a valid username + """ + view = UserModelOpenIDConnectViewset.as_view({"post": "callback"}) + with patch( + "oidc.viewsets.OpenIDClient.verify_and_decode_id_token" + ) as mock_func: + mock_func.return_value = { + "family_name": "bob", + "given_name": "just bob", + "username": "bob@example.com", + "email": "bo@example.com", + } + + data = {"id_token": "sadsdaio3209lkasdlkas0d.sdojdsiad.iosdadia"} + request = self.factory.post("/", data=data) + response = view(request, auth_server="default") + self.assertEqual(response.status_code, 400) + self.assertEqual(response.template_name, "oidc/oidc_user_data_entry.html") + def test_unrecoverable_error_on_missing_claim(self): """ Test that an error is returned when a required claim field other than the