PageRenderTime 36ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/daycapture_venv/lib/python3.8/site-packages/django/middleware/common.py

https://gitlab.com/llndhlov/journly
Python | 174 lines | 170 code | 3 blank | 1 comment | 2 complexity | 6f6369a35fdd816b514a8ffef71b25bb MD5 | raw file
  1. import re
  2. from urllib.parse import urlparse
  3. from django.conf import settings
  4. from django.core.exceptions import PermissionDenied
  5. from django.core.mail import mail_managers
  6. from django.http import HttpResponsePermanentRedirect
  7. from django.urls import is_valid_path
  8. from django.utils.deprecation import MiddlewareMixin
  9. from django.utils.http import escape_leading_slashes
  10. class CommonMiddleware(MiddlewareMixin):
  11. """
  12. "Common" middleware for taking care of some basic operations:
  13. - Forbid access to User-Agents in settings.DISALLOWED_USER_AGENTS
  14. - URL rewriting: Based on the APPEND_SLASH and PREPEND_WWW settings,
  15. append missing slashes and/or prepends missing "www."s.
  16. - If APPEND_SLASH is set and the initial URL doesn't end with a
  17. slash, and it is not found in urlpatterns, form a new URL by
  18. appending a slash at the end. If this new URL is found in
  19. urlpatterns, return an HTTP redirect to this new URL; otherwise
  20. process the initial URL as usual.
  21. This behavior can be customized by subclassing CommonMiddleware and
  22. overriding the response_redirect_class attribute.
  23. """
  24. response_redirect_class = HttpResponsePermanentRedirect
  25. def process_request(self, request):
  26. """
  27. Check for denied User-Agents and rewrite the URL based on
  28. settings.APPEND_SLASH and settings.PREPEND_WWW
  29. """
  30. # Check for denied User-Agents
  31. user_agent = request.META.get('HTTP_USER_AGENT')
  32. if user_agent is not None:
  33. for user_agent_regex in settings.DISALLOWED_USER_AGENTS:
  34. if user_agent_regex.search(user_agent):
  35. raise PermissionDenied('Forbidden user agent')
  36. # Check for a redirect based on settings.PREPEND_WWW
  37. host = request.get_host()
  38. must_prepend = settings.PREPEND_WWW and host and not host.startswith('www.')
  39. redirect_url = ('%s://www.%s' % (request.scheme, host)) if must_prepend else ''
  40. # Check if a slash should be appended
  41. if self.should_redirect_with_slash(request):
  42. path = self.get_full_path_with_slash(request)
  43. else:
  44. path = request.get_full_path()
  45. # Return a redirect if necessary
  46. if redirect_url or path != request.get_full_path():
  47. redirect_url += path
  48. return self.response_redirect_class(redirect_url)
  49. def should_redirect_with_slash(self, request):
  50. """
  51. Return True if settings.APPEND_SLASH is True and appending a slash to
  52. the request path turns an invalid path into a valid one.
  53. """
  54. if settings.APPEND_SLASH and not request.path_info.endswith('/'):
  55. urlconf = getattr(request, 'urlconf', None)
  56. if not is_valid_path(request.path_info, urlconf):
  57. match = is_valid_path('%s/' % request.path_info, urlconf)
  58. if match:
  59. view = match.func
  60. return getattr(view, 'should_append_slash', True)
  61. return False
  62. def get_full_path_with_slash(self, request):
  63. """
  64. Return the full path of the request with a trailing slash appended.
  65. Raise a RuntimeError if settings.DEBUG is True and request.method is
  66. POST, PUT, or PATCH.
  67. """
  68. new_path = request.get_full_path(force_append_slash=True)
  69. # Prevent construction of scheme relative urls.
  70. new_path = escape_leading_slashes(new_path)
  71. if settings.DEBUG and request.method in ('POST', 'PUT', 'PATCH'):
  72. raise RuntimeError(
  73. "You called this URL via %(method)s, but the URL doesn't end "
  74. "in a slash and you have APPEND_SLASH set. Django can't "
  75. "redirect to the slash URL while maintaining %(method)s data. "
  76. "Change your form to point to %(url)s (note the trailing "
  77. "slash), or set APPEND_SLASH=False in your Django settings." % {
  78. 'method': request.method,
  79. 'url': request.get_host() + new_path,
  80. }
  81. )
  82. return new_path
  83. def process_response(self, request, response):
  84. """
  85. When the status code of the response is 404, it may redirect to a path
  86. with an appended slash if should_redirect_with_slash() returns True.
  87. """
  88. # If the given URL is "Not Found", then check if we should redirect to
  89. # a path with a slash appended.
  90. if response.status_code == 404 and self.should_redirect_with_slash(request):
  91. return self.response_redirect_class(self.get_full_path_with_slash(request))
  92. # Add the Content-Length header to non-streaming responses if not
  93. # already set.
  94. if not response.streaming and not response.has_header('Content-Length'):
  95. response.headers['Content-Length'] = str(len(response.content))
  96. return response
  97. class BrokenLinkEmailsMiddleware(MiddlewareMixin):
  98. def process_response(self, request, response):
  99. """Send broken link emails for relevant 404 NOT FOUND responses."""
  100. if response.status_code == 404 and not settings.DEBUG:
  101. domain = request.get_host()
  102. path = request.get_full_path()
  103. referer = request.META.get('HTTP_REFERER', '')
  104. if not self.is_ignorable_request(request, path, domain, referer):
  105. ua = request.META.get('HTTP_USER_AGENT', '<none>')
  106. ip = request.META.get('REMOTE_ADDR', '<none>')
  107. mail_managers(
  108. "Broken %slink on %s" % (
  109. ('INTERNAL ' if self.is_internal_request(domain, referer) else ''),
  110. domain
  111. ),
  112. "Referrer: %s\nRequested URL: %s\nUser agent: %s\n"
  113. "IP address: %s\n" % (referer, path, ua, ip),
  114. fail_silently=True,
  115. )
  116. return response
  117. def is_internal_request(self, domain, referer):
  118. """
  119. Return True if the referring URL is the same domain as the current
  120. request.
  121. """
  122. # Different subdomains are treated as different domains.
  123. return bool(re.match("^https?://%s/" % re.escape(domain), referer))
  124. def is_ignorable_request(self, request, uri, domain, referer):
  125. """
  126. Return True if the given request *shouldn't* notify the site managers
  127. according to project settings or in situations outlined by the inline
  128. comments.
  129. """
  130. # The referer is empty.
  131. if not referer:
  132. return True
  133. # APPEND_SLASH is enabled and the referer is equal to the current URL
  134. # without a trailing slash indicating an internal redirect.
  135. if settings.APPEND_SLASH and uri.endswith('/') and referer == uri[:-1]:
  136. return True
  137. # A '?' in referer is identified as a search engine source.
  138. if not self.is_internal_request(domain, referer) and '?' in referer:
  139. return True
  140. # The referer is equal to the current URL, ignoring the scheme (assumed
  141. # to be a poorly implemented bot).
  142. parsed_referer = urlparse(referer)
  143. if parsed_referer.netloc in ['', domain] and parsed_referer.path == uri:
  144. return True
  145. return any(pattern.search(uri) for pattern in settings.IGNORABLE_404_URLS)