diff --git a/netbox/netbox/middleware.py b/netbox/netbox/middleware.py index 68c01245c..a8649c629 100644 --- a/netbox/netbox/middleware.py +++ b/netbox/netbox/middleware.py @@ -40,15 +40,24 @@ class CoreMiddleware: with apply_request_processors(request): response = self.get_response(request) - # Check if language cookie should be renewed - if request.user.is_authenticated and settings.SESSION_SAVE_EVERY_REQUEST: - if language := request.user.config.get('locale.language'): - response.set_cookie( - key=settings.LANGUAGE_COOKIE_NAME, - value=language, - max_age=request.session.get_expiry_age(), - secure=settings.SESSION_COOKIE_SECURE, - ) + # Set or renew the language cookie based on the user's preference. This handles two cases: + # 1. The user just logged in (via any auth backend): the user_logged_in signal stores the preferred language on + # the request so we set the cookie here on the login response. + # 2. SESSION_SAVE_EVERY_REQUEST is enabled: renew the language cookie on every request to keep it in sync with + # the session expiry. + if hasattr(request, '_language_cookie'): + language = request._language_cookie + elif request.user.is_authenticated and settings.SESSION_SAVE_EVERY_REQUEST: + language = request.user.config.get('locale.language') + else: + language = None + if language: + response.set_cookie( + key=settings.LANGUAGE_COOKIE_NAME, + value=language, + max_age=request.session.get_expiry_age(), + secure=settings.SESSION_COOKIE_SECURE, + ) # Attach the unique request ID as an HTTP header. response['X-Request-ID'] = request.id diff --git a/netbox/users/signals.py b/netbox/users/signals.py index 83d57b4e3..e195cded9 100644 --- a/netbox/users/signals.py +++ b/netbox/users/signals.py @@ -1,6 +1,6 @@ import logging -from django.contrib.auth.signals import 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.dispatch import receiver @@ -23,6 +23,18 @@ def log_user_login_failed(sender, credentials, request, **kwargs): logger.info(f"Failed login attempt for username: {username}") +@receiver(user_logged_in) +def set_language_on_login(sender, user, request, **kwargs): + """ + Store the user's preferred language on the request so that middleware can set the language cookie. This ensures the + language preference is applied even when logging in via an external auth provider (e.g. social-app-django) that + does not go through NetBox's LoginView. + """ + if hasattr(user, 'config'): + if language := user.config.get('locale.language'): + request._language_cookie = language + + @receiver(post_save, sender=User) def create_userconfig(instance, created, raw=False, **kwargs): """