middleware.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. from urllib import parse
  2. from django.conf import settings
  3. from django.contrib.auth.middleware import RemoteUserMiddleware as RemoteUserMiddleware_
  4. from django.db import ProgrammingError
  5. from django.http import Http404, HttpResponseRedirect
  6. from django.urls import reverse
  7. from .api import is_api_request
  8. from .views import server_error
  9. class LoginRequiredMiddleware(object):
  10. """
  11. If LOGIN_REQUIRED is True, redirect all non-authenticated users to the login page.
  12. """
  13. def __init__(self, get_response):
  14. self.get_response = get_response
  15. def __call__(self, request):
  16. if settings.LOGIN_REQUIRED and not request.user.is_authenticated:
  17. # Redirect unauthenticated requests to the login page. API requests are exempt from redirection as the API
  18. # performs its own authentication. Also metrics can be read without login.
  19. api_path = reverse('api-root')
  20. if not request.path_info.startswith((api_path, '/metrics')) and request.path_info != settings.LOGIN_URL:
  21. return HttpResponseRedirect(
  22. '{}?next={}'.format(
  23. settings.LOGIN_URL,
  24. parse.quote(request.get_full_path_info())
  25. )
  26. )
  27. return self.get_response(request)
  28. class RemoteUserMiddleware(RemoteUserMiddleware_):
  29. """
  30. Custom implementation of Django's RemoteUserMiddleware which allows for a user-configurable HTTP header name.
  31. """
  32. force_logout_if_no_header = False
  33. @property
  34. def header(self):
  35. return settings.REMOTE_AUTH_HEADER
  36. def process_request(self, request):
  37. # Bypass middleware if remote authentication is not enabled
  38. if not settings.REMOTE_AUTH_ENABLED:
  39. return
  40. return super().process_request(request)
  41. class APIVersionMiddleware(object):
  42. """
  43. If the request is for an API endpoint, include the API version as a response header.
  44. """
  45. def __init__(self, get_response):
  46. self.get_response = get_response
  47. def __call__(self, request):
  48. response = self.get_response(request)
  49. if is_api_request(request):
  50. response['API-Version'] = settings.REST_FRAMEWORK_VERSION
  51. return response
  52. class ExceptionHandlingMiddleware(object):
  53. """
  54. Intercept certain exceptions which are likely indicative of installation issues and provide helpful instructions
  55. to the user.
  56. """
  57. def __init__(self, get_response):
  58. self.get_response = get_response
  59. def __call__(self, request):
  60. return self.get_response(request)
  61. def process_exception(self, request, exception):
  62. # Don't catch exceptions when in debug mode
  63. if settings.DEBUG:
  64. return
  65. # Ignore Http404s (defer to Django's built-in 404 handling)
  66. if isinstance(exception, Http404):
  67. return
  68. # Determine the type of exception. If it's a common issue, return a custom error page with instructions.
  69. custom_template = None
  70. if isinstance(exception, ProgrammingError):
  71. custom_template = 'exceptions/programming_error.html'
  72. elif isinstance(exception, ImportError):
  73. custom_template = 'exceptions/import_error.html'
  74. elif isinstance(exception, PermissionError):
  75. custom_template = 'exceptions/permission_error.html'
  76. # Return a custom error message, or fall back to Django's default 500 error handling
  77. if custom_template:
  78. return server_error(request, template_name=custom_template)