request.py 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. from django.utils.translation import gettext_lazy as _
  2. from netaddr import AddrFormatError, IPAddress
  3. from urllib.parse import urlparse
  4. from .constants import HTTP_REQUEST_META_SAFE_COPY
  5. __all__ = (
  6. 'NetBoxFakeRequest',
  7. 'copy_safe_request',
  8. 'get_client_ip',
  9. )
  10. #
  11. # Fake request object
  12. #
  13. class NetBoxFakeRequest:
  14. """
  15. A fake request object which is explicitly defined at the module level so it is able to be pickled. It simply
  16. takes what is passed to it as kwargs on init and sets them as instance variables.
  17. """
  18. def __init__(self, _dict):
  19. self.__dict__ = _dict
  20. #
  21. # Utility functions
  22. #
  23. def copy_safe_request(request):
  24. """
  25. Copy selected attributes from a request object into a new fake request object. This is needed in places where
  26. thread safe pickling of the useful request data is needed.
  27. """
  28. meta = {
  29. k: request.META[k]
  30. for k in HTTP_REQUEST_META_SAFE_COPY
  31. if k in request.META and isinstance(request.META[k], str)
  32. }
  33. return NetBoxFakeRequest({
  34. 'META': meta,
  35. 'COOKIES': request.COOKIES,
  36. 'POST': request.POST,
  37. 'GET': request.GET,
  38. 'FILES': request.FILES,
  39. 'user': request.user,
  40. 'path': request.path,
  41. 'id': getattr(request, 'id', None), # UUID assigned by middleware
  42. })
  43. def get_client_ip(request, additional_headers=()):
  44. """
  45. Return the client (source) IP address of the given request.
  46. """
  47. HTTP_HEADERS = (
  48. 'HTTP_X_REAL_IP',
  49. 'HTTP_X_FORWARDED_FOR',
  50. 'REMOTE_ADDR',
  51. *additional_headers
  52. )
  53. for header in HTTP_HEADERS:
  54. if header in request.META:
  55. ip = request.META[header].split(',')[0].strip()
  56. try:
  57. return IPAddress(ip)
  58. except AddrFormatError:
  59. # Parse the string with urlparse() to remove port number or any other cruft
  60. ip = urlparse(f'//{ip}').hostname
  61. try:
  62. return IPAddress(ip)
  63. except AddrFormatError:
  64. # We did our best
  65. raise ValueError(_("Invalid IP address set for {header}: {ip}").format(header=header, ip=ip))
  66. # Could not determine the client IP address from request headers
  67. return None