From 1cb10eeb4fc2bbd0a60030ecaa69acc9994c1a26 Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Thu, 7 Aug 2025 13:20:50 -0500 Subject: [PATCH 1/4] Fixes: #19669 & #18396 - Allow Token Authentication against Media view --- netbox/netbox/views/misc.py | 4 ++-- netbox/utilities/views.py | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/netbox/netbox/views/misc.py b/netbox/netbox/views/misc.py index f28b0f7b155..ab995db6654 100644 --- a/netbox/netbox/views/misc.py +++ b/netbox/netbox/views/misc.py @@ -20,7 +20,7 @@ from netbox.tables import SearchTable from utilities.htmx import htmx_partial from utilities.paginator import EnhancedPaginator, get_paginate_count -from utilities.views import ConditionalLoginRequiredMixin +from utilities.views import ConditionalLoginRequiredMixin, TokenConditionalLoginRequiredMixin __all__ = ( 'HomeView', @@ -119,7 +119,7 @@ def get(self, request): }) -class MediaView(ConditionalLoginRequiredMixin, View): +class MediaView(TokenConditionalLoginRequiredMixin, View): """ Wrap Django's serve() view to enforce LOGIN_REQUIRED for static media. """ diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py index 1263874c463..631d0b7bc72 100644 --- a/netbox/utilities/views.py +++ b/netbox/utilities/views.py @@ -6,7 +6,9 @@ from django.urls import reverse from django.urls.exceptions import NoReverseMatch from django.utils.translation import gettext_lazy as _ +from rest_framework.exceptions import AuthenticationFailed +from netbox.api.authentication import TokenAuthentication from netbox.plugins import PluginConfig from netbox.registry import registry from utilities.relations import get_related_models @@ -19,6 +21,7 @@ 'GetRelatedModelsMixin', 'GetReturnURLMixin', 'ObjectPermissionRequiredMixin', + 'TokenConditionalLoginRequiredMixin', 'ViewTab', 'get_viewname', 'register_model_view', @@ -39,6 +42,21 @@ def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs) +class TokenConditionalLoginRequiredMixin(ConditionalLoginRequiredMixin): + def dispatch(self, request, *args, **kwargs): + # Attempt to authenticate the user using a DRF token, if provided + if settings.LOGIN_REQUIRED and not request.user.is_authenticated: + authenticator = TokenAuthentication() + try: + auth_info = authenticator.authenticate(request) + if auth_info is not None: + request.user = auth_info[0] # User object + except AuthenticationFailed: + pass + + return super().dispatch(request, *args, **kwargs) + + class ContentTypePermissionRequiredMixin(ConditionalLoginRequiredMixin): """ Similar to Django's built-in PermissionRequiredMixin, but extended to check model-level permission assignments. From 244e738ebb4f5199695f13eac0e8fb8521a85f6d Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Mon, 11 Aug 2025 13:00:43 -0500 Subject: [PATCH 2/4] Update media view to preserve the exception. --- netbox/utilities/views.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py index 631d0b7bc72..4c19ce7e080 100644 --- a/netbox/utilities/views.py +++ b/netbox/utilities/views.py @@ -6,7 +6,6 @@ from django.urls import reverse from django.urls.exceptions import NoReverseMatch from django.utils.translation import gettext_lazy as _ -from rest_framework.exceptions import AuthenticationFailed from netbox.api.authentication import TokenAuthentication from netbox.plugins import PluginConfig @@ -47,12 +46,9 @@ def dispatch(self, request, *args, **kwargs): # Attempt to authenticate the user using a DRF token, if provided if settings.LOGIN_REQUIRED and not request.user.is_authenticated: authenticator = TokenAuthentication() - try: - auth_info = authenticator.authenticate(request) - if auth_info is not None: - request.user = auth_info[0] # User object - except AuthenticationFailed: - pass + auth_info = authenticator.authenticate(request) + if auth_info is not None: + request.user = auth_info[0] # User object return super().dispatch(request, *args, **kwargs) From 2192a7d8d74a145c572d97741a16aac1b316b6e8 Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Mon, 11 Aug 2025 13:45:12 -0500 Subject: [PATCH 3/4] Set `request.auth` to the `Token` instance --- netbox/utilities/views.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py index 4c19ce7e080..64190b6ba18 100644 --- a/netbox/utilities/views.py +++ b/netbox/utilities/views.py @@ -49,6 +49,10 @@ def dispatch(self, request, *args, **kwargs): auth_info = authenticator.authenticate(request) if auth_info is not None: request.user = auth_info[0] # User object + try: + request.auth = auth_info[1] + except IndexError: + pass return super().dispatch(request, *args, **kwargs) From a9e2872d0f6208d00c7a778c678ebc90d8e26305 Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Wed, 13 Aug 2025 22:09:21 -0500 Subject: [PATCH 4/4] Update per discussion --- netbox/utilities/views.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py index 64190b6ba18..5c6c7977d58 100644 --- a/netbox/utilities/views.py +++ b/netbox/utilities/views.py @@ -49,10 +49,7 @@ def dispatch(self, request, *args, **kwargs): auth_info = authenticator.authenticate(request) if auth_info is not None: request.user = auth_info[0] # User object - try: - request.auth = auth_info[1] - except IndexError: - pass + request.auth = auth_info[1] return super().dispatch(request, *args, **kwargs)