Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions dojo/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ def process_exception(self, request, exception):
if isinstance(exception, AuthForbidden):
messages.error(request, "You are not authorized to log in via this method. Please contact support or use the standard login.")
return redirect("/login?force_login_form")
if isinstance(exception, TypeError) and "'NoneType' object is not iterable" in str(exception):
logger.warning("OIDC login error: NoneType is not iterable")
messages.error(request, "An unexpected error occurred during social login. Please use the standard login.")
return redirect("/login?force_login_form")
logger.error(f"Unhandled exception during social login: {exception}")
return super().process_exception(request, exception)


Expand Down
3 changes: 2 additions & 1 deletion dojo/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,5 +183,6 @@ def sanitize_username(username):
def create_user(strategy, details, backend, user=None, *args, **kwargs):
if not settings.SOCIAL_AUTH_CREATE_USER:
return None
details["username"] = sanitize_username(details.get("username"))
username = details.get(settings.SOCIAL_AUTH_CREATE_USER_MAPPING)
details["username"] = sanitize_username(username)
return social_core.pipeline.user.create_user(strategy, details, backend, user, args, kwargs)
2 changes: 2 additions & 0 deletions dojo/settings/settings.dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
DD_FORGOT_USERNAME=(bool, True), # do we show link "I forgot my username" on login screen
DD_SOCIAL_AUTH_SHOW_LOGIN_FORM=(bool, True), # do we show user/pass input
DD_SOCIAL_AUTH_CREATE_USER=(bool, True), # if True creates user at first login
DD_SOCIAL_AUTH_CREATE_USER_MAPPING=(str, "username"), # could also be email or fullname
DD_SOCIAL_LOGIN_AUTO_REDIRECT=(bool, False), # auto-redirect if there is only one social login method
DD_SOCIAL_AUTH_TRAILING_SLASH=(bool, True),
DD_SOCIAL_AUTH_OIDC_AUTH_ENABLED=(bool, False),
Expand Down Expand Up @@ -574,6 +575,7 @@ def generate_url(scheme, double_slashes, user, password, host, port, path, param
SHOW_LOGIN_FORM = env("DD_SOCIAL_AUTH_SHOW_LOGIN_FORM")
SOCIAL_LOGIN_AUTO_REDIRECT = env("DD_SOCIAL_LOGIN_AUTO_REDIRECT")
SOCIAL_AUTH_CREATE_USER = env("DD_SOCIAL_AUTH_CREATE_USER")
SOCIAL_AUTH_CREATE_USER_MAPPING = env("DD_SOCIAL_AUTH_CREATE_USER_MAPPING")

SOCIAL_AUTH_STRATEGY = "social_django.strategy.DjangoStrategy"
SOCIAL_AUTH_STORAGE = "social_django.models.DjangoStorage"
Expand Down
10 changes: 10 additions & 0 deletions unittests/test_social_auth_failure_handling.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,16 @@ def test_non_social_auth_path_redirects_on_auth_forbidden(self):
storage = list(messages.get_messages(request))
self.assertTrue(any("You are not authorized to log in via this method." in str(msg) for msg in storage))

def test_type_error_none_type_iterable_redirect(self):
"""Ensure middleware catches 'NoneType' object is not iterable TypeError and redirects."""
request = self._prepare_request("/login/oidc/")
exception = TypeError("'NoneType' object is not iterable")
response = self.middleware.process_exception(request, exception)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, "/login?force_login_form")
storage = list(messages.get_messages(request))
self.assertTrue(any("An unexpected error occurred during social login." in str(msg) for msg in storage))


@override_settings(
AUTHENTICATION_BACKENDS=(
Expand Down