views.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. from django.http import Http404
  2. from activitypub.signatures import Signature
  3. import json
  4. import logging
  5. import re
  6. from django.core.exceptions import BadRequest, PermissionDenied
  7. from django.utils.decorators import method_decorator
  8. from django.views import View
  9. from django.views.decorators.csrf import csrf_exempt
  10. from activitypub.exceptions import (
  11. BannedOrDeletedActorException,
  12. BlockedActorException,
  13. BlockedUserAgentException,
  14. )
  15. from activitypub.models import FederatedServer
  16. logger = logging.getLogger(__name__)
  17. @method_decorator(csrf_exempt, name="dispatch")
  18. # pylint: disable=no-self-use
  19. class InboxView(View):
  20. """requests sent by outside servers"""
  21. def post(self, request, username=None):
  22. """InboxView handles requests sent from outside our instance"""
  23. self.check_is_blocked_user_agent()
  24. # make sure the user's inbox even exists
  25. if username:
  26. get_object_or_404(User, localname=username, is_active=True)
  27. # is it valid json? does it at least vaguely resemble an activity?
  28. try:
  29. activity_json = json.loads(request.body)
  30. except json.decoder.JSONDecodeError:
  31. raise BadRequest()
  32. # let's be extra sure we didn't block this domain
  33. self.check_is_blocked_activity(activity_json)
  34. if (
  35. not "object" in activity_json
  36. or not "type" in activity_json
  37. or not activity_json["type"] in activitypub.activity_objects
  38. ):
  39. raise Http404()
  40. # verify the signature
  41. if not has_valid_signature(request, activity_json):
  42. if activity_json["type"] == "Delete":
  43. # Pretend that unauth'd deletes succeed. Auth may be failing
  44. # because the resource or owner of the resource might have
  45. # been deleted.
  46. return HttpResponse()
  47. return HttpResponse(status=401)
  48. activity_task.delay(activity_json)
  49. return HttpResponse()
  50. def check_is_blocked_user_agent(self) -> None:
  51. """Raise an exception if a request is from a blocked server based on user agent"""
  52. user_agent = self.request.headers.get("User-Agent")
  53. if not user_agent:
  54. return
  55. url = re.search(rf"https?://{regex.DOMAIN}/?", user_agent)
  56. if not url:
  57. return
  58. url = url.group()
  59. if FederatedServer.is_blocked(url):
  60. logger.debug(
  61. "%s is blocked, denying request based on user agent", url
  62. )
  63. raise BlockedUserAgentException
  64. def check_is_blocked_activity(self, activity_json: dict) -> None:
  65. """Raise an exception if actor of an activity is blocked"""
  66. actor = activity_json.get("actor")
  67. if not actor:
  68. return
  69. # TODO Remove User hard-code and add an AP Profile
  70. existing = User.find_existing_by_remote_id(actor)
  71. if existing:
  72. if existing.deleted:
  73. logger.debug("%s is banned/deleted", actor)
  74. raise BannedOrDeletedActorException
  75. if existing.banned:
  76. logger.debug("%s is banned/deleted", actor)
  77. raise BannedOrDeletedActorException
  78. if FederatedServer.is_blocked(actor):
  79. logger.debug("%s is blocked", actor)
  80. raise BlockedActorException
  81. def is_signature_valid(self, activity) -> bool:
  82. try:
  83. signature = Signature.parse(self.request.headers["Signature"])
  84. key_actor = urldefrag(signature.key_id).url
  85. if key_actor != activity.get("actor"):
  86. raise ValueError("Wrong actor created signature.")
  87. remote_user = resolve_remote_id(key_actor, model=User)
  88. if not remote_user:
  89. return False
  90. try:
  91. signature.verify(remote_user.key_pair.public_key, self.request)
  92. except ValueError:
  93. old_key = remote_user.key_pair.public_key
  94. remote_user = resolve_remote_id(
  95. remote_user.remote_id, model=models.User, refresh=True
  96. )
  97. if remote_user.key_pair.public_key == old_key:
  98. raise # Key unchanged.
  99. signature.verify(remote_user.key_pair.public_key, self.request)
  100. except (ValueError, requests.exceptions.HTTPError):
  101. return False
  102. return True