Ver Fonte

Closes #11489: Refactor & combine core middleware

jeremystretch há 3 anos atrás
pai
commit
0b4ea14e9a
2 ficheiros alterados com 62 adições e 115 exclusões
  1. 60 109
      netbox/netbox/middleware.py
  2. 2 6
      netbox/netbox/settings.py

+ 60 - 109
netbox/netbox/middleware.py

@@ -14,24 +14,73 @@ from netbox.config import clear_config
 from netbox.views import handler_500
 from utilities.api import is_api_request, rest_api_server_error
 
+__all__ = (
+    'CoreMiddleware',
+    'RemoteUserMiddleware',
+)
+
+
+class CoreMiddleware:
 
-class LoginRequiredMiddleware:
-    """
-    If LOGIN_REQUIRED is True, redirect all non-authenticated users to the login page.
-    """
     def __init__(self, get_response):
         self.get_response = get_response
 
     def __call__(self, request):
-        # Redirect unauthenticated requests (except those exempted) to the login page if LOGIN_REQUIRED is true
-        if settings.LOGIN_REQUIRED and not request.user.is_authenticated:
 
-            # Redirect unauthenticated requests
-            if not request.path_info.startswith(settings.EXEMPT_PATHS):
-                login_url = f'{settings.LOGIN_URL}?next={parse.quote(request.get_full_path_info())}'
-                return HttpResponseRedirect(login_url)
+        # Assign a random unique ID to the request. This will be used for change logging.
+        request.id = uuid.uuid4()
+
+        # Enforce the LOGIN_REQUIRED config parameter. If true, redirect all non-exempt unauthenticated requests
+        # to the login page.
+        if (
+            settings.LOGIN_REQUIRED and
+            not request.user.is_authenticated and
+            not request.path_info.startswith(settings.AUTH_EXEMPT_PATHS)
+        ):
+            login_url = f'{settings.LOGIN_URL}?next={parse.quote(request.get_full_path_info())}'
+            return HttpResponseRedirect(login_url)
+
+        # Enable the change_logging context manager and process the request.
+        with change_logging(request):
+            response = self.get_response(request)
+
+        # If this is an API request, attach an HTTP header annotating the API version (e.g. '3.5').
+        if is_api_request(request):
+            response['API-Version'] = settings.REST_FRAMEWORK_VERSION
+
+        # Clear any cached dynamic config parameters after each request.
+        clear_config()
+
+        return response
 
-        return self.get_response(request)
+    def process_exception(self, request, exception):
+        """
+        Implement custom error handling logic for production deployments.
+        """
+        # Don't catch exceptions when in debug mode
+        if settings.DEBUG:
+            return
+
+        # Cleanly handle exceptions that occur from REST API requests
+        if is_api_request(request):
+            return rest_api_server_error(request)
+
+        # Ignore Http404s (defer to Django's built-in 404 handling)
+        if isinstance(exception, Http404):
+            return
+
+        # Determine the type of exception. If it's a common issue, return a custom error page with instructions.
+        custom_template = None
+        if isinstance(exception, ProgrammingError):
+            custom_template = 'exceptions/programming_error.html'
+        elif isinstance(exception, ImportError):
+            custom_template = 'exceptions/import_error.html'
+        elif isinstance(exception, PermissionError):
+            custom_template = 'exceptions/permission_error.html'
+
+        # Return a custom error message, or fall back to Django's default 500 error handling
+        if custom_template:
+            return handler_500(request, template_name=custom_template)
 
 
 class RemoteUserMiddleware(RemoteUserMiddleware_):
@@ -104,101 +153,3 @@ class RemoteUserMiddleware(RemoteUserMiddleware_):
             groups = []
         logger.debug(f"Groups are {groups}")
         return groups
-
-
-class ObjectChangeMiddleware:
-    """
-    This middleware performs three functions in response to an object being created, updated, or deleted:
-
-        1. Create an ObjectChange to reflect the modification to the object in the changelog.
-        2. Enqueue any relevant webhooks.
-        3. Increment the metric counter for the event type.
-
-    The post_save and post_delete signals are employed to catch object modifications, however changes are recorded a bit
-    differently for each. Objects being saved are cached into thread-local storage for action *after* the response has
-    completed. This ensures that serialization of the object is performed only after any related objects (e.g. tags)
-    have been created. Conversely, deletions are acted upon immediately, so that the serialized representation of the
-    object is recorded before it (and any related objects) are actually deleted from the database.
-    """
-
-    def __init__(self, get_response):
-        self.get_response = get_response
-
-    def __call__(self, request):
-        # Assign a random unique ID to the request. This will be used to associate multiple object changes made during
-        # the same request.
-        request.id = uuid.uuid4()
-
-        # Process the request with change logging enabled
-        with change_logging(request):
-            response = self.get_response(request)
-
-        return response
-
-
-class APIVersionMiddleware:
-    """
-    If the request is for an API endpoint, include the API version as a response header.
-    """
-
-    def __init__(self, get_response):
-        self.get_response = get_response
-
-    def __call__(self, request):
-        response = self.get_response(request)
-        if is_api_request(request):
-            response['API-Version'] = settings.REST_FRAMEWORK_VERSION
-        return response
-
-
-class DynamicConfigMiddleware:
-    """
-    Store the cached NetBox configuration in thread-local storage for the duration of the request.
-    """
-    def __init__(self, get_response):
-        self.get_response = get_response
-
-    def __call__(self, request):
-        response = self.get_response(request)
-        clear_config()
-        return response
-
-
-class ExceptionHandlingMiddleware:
-    """
-    Intercept certain exceptions which are likely indicative of installation issues and provide helpful instructions
-    to the user.
-    """
-
-    def __init__(self, get_response):
-        self.get_response = get_response
-
-    def __call__(self, request):
-        return self.get_response(request)
-
-    def process_exception(self, request, exception):
-
-        # Handle exceptions that occur from REST API requests
-        # if is_api_request(request):
-        #     return rest_api_server_error(request)
-
-        # Don't catch exceptions when in debug mode
-        if settings.DEBUG:
-            return
-
-        # Ignore Http404s (defer to Django's built-in 404 handling)
-        if isinstance(exception, Http404):
-            return
-
-        # Determine the type of exception. If it's a common issue, return a custom error page with instructions.
-        custom_template = None
-        if isinstance(exception, ProgrammingError):
-            custom_template = 'exceptions/programming_error.html'
-        elif isinstance(exception, ImportError):
-            custom_template = 'exceptions/import_error.html'
-        elif isinstance(exception, PermissionError):
-            custom_template = 'exceptions/permission_error.html'
-
-        # Return a custom error message, or fall back to Django's default 500 error handling
-        if custom_template:
-            return handler_500(request, template_name=custom_template)

+ 2 - 6
netbox/netbox/settings.py

@@ -358,12 +358,8 @@ MIDDLEWARE = [
     'django.contrib.messages.middleware.MessageMiddleware',
     'django.middleware.clickjacking.XFrameOptionsMiddleware',
     'django.middleware.security.SecurityMiddleware',
-    'netbox.middleware.ExceptionHandlingMiddleware',
     'netbox.middleware.RemoteUserMiddleware',
-    'netbox.middleware.LoginRequiredMiddleware',
-    'netbox.middleware.DynamicConfigMiddleware',
-    'netbox.middleware.APIVersionMiddleware',
-    'netbox.middleware.ObjectChangeMiddleware',
+    'netbox.middleware.CoreMiddleware',
     'django_prometheus.middleware.PrometheusAfterMiddleware',
 ]
 
@@ -448,7 +444,7 @@ EXEMPT_EXCLUDE_MODELS = (
 )
 
 # All URLs starting with a string listed here are exempt from login enforcement
-EXEMPT_PATHS = (
+AUTH_EXEMPT_PATHS = (
     f'/{BASE_PATH}api/',
     f'/{BASE_PATH}graphql/',
     f'/{BASE_PATH}login/',