From 5c69288d48d2a54cd125e26285b8da3387d5b7d9 Mon Sep 17 00:00:00 2001 From: Reid 'arrdem' McKenzie Date: Sat, 26 Nov 2022 17:24:53 -0700 Subject: [PATCH] Working towards a queue of follow requests --- .../src/python/relay/database.py | 14 ++++++++------ .../src/python/relay/processors.py | 19 +++++++++++++++---- .../src/python/relay/views.py | 17 +++++------------ 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/projects/activitypub_relay/src/python/relay/database.py b/projects/activitypub_relay/src/python/relay/database.py index df42c61..16cd2a0 100644 --- a/projects/activitypub_relay/src/python/relay/database.py +++ b/projects/activitypub_relay/src/python/relay/database.py @@ -39,7 +39,6 @@ class RelayDatabase(dict): @property def inboxes(self): return tuple(data["inbox"] for data in self["relay-list"].values()) - return self["relay-list"] def generate_key(self): self.PRIVKEY = RSA.generate(4096) @@ -152,8 +151,8 @@ class RelayDatabase(dict): return False def set_followid(self, domain, followid): - data = self.get_inbox(domain, fail=True) - data["followid"] = followid + if (data := self.get_inbox(domain, fail=True)): + data["followid"] = followid def get_request(self, domain, fail=True): if domain.startswith("http"): @@ -170,8 +169,8 @@ class RelayDatabase(dict): domain = urlparse(inbox).hostname try: - request = self.get_request(domain) - request["followid"] = followid + if (request := self.get_request(domain)): + request["followid"] = followid except KeyError: pass @@ -184,6 +183,9 @@ class RelayDatabase(dict): def del_request(self, domain): if domain.startswith("http"): - domain = urlparse(inbox).hostname + domain = urlparse(domain).hostname del self["follow-requests"][domain] + + def get_requests(self): + return list(self["follow-requests"].items()) diff --git a/projects/activitypub_relay/src/python/relay/processors.py b/projects/activitypub_relay/src/python/relay/processors.py index 741c813..57649a2 100644 --- a/projects/activitypub_relay/src/python/relay/processors.py +++ b/projects/activitypub_relay/src/python/relay/processors.py @@ -2,6 +2,8 @@ import asyncio import logging from relay import misc +from relay.config import RelayConfig +from relay.database import RelayDatabase async def handle_relay(request, actor, data, software): @@ -42,10 +44,19 @@ async def handle_forward(request, actor, data, software): async def handle_follow(request, actor, data, software): - if not request.app.database.add_inbox(actor.shared_inbox, data.id): - request.app.database.set_followid(actor.id, data.id) + config: RelayConfig = request.app.config + database: RelayDatabase = request.app.database - request.app.database.save() + # If the following host is not whitelisted, we want to enqueue the request for review. + # This means saving off the two parameters we need later to issue an appropriate acceptance. + if config.whitelist_enabled and not config.is_whitelisted(data.domain): + database.add_request(actor.id, actor.shared_inbox, data.id) + database.save() + return + + if not database.add_inbox(actor.shared_inbox, data.id): + database.set_followid(actor.id, data.id) + database.save() await misc.request( actor.shared_inbox, @@ -67,7 +78,7 @@ async def handle_follow(request, actor, data, software): async def handle_undo(request, actor, data, software): - ## If the object is not a Follow, forward it + # If the object is not a Follow, forward it if data["object"]["type"] != "Follow": return await handle_forward(request, actor, data, software) diff --git a/projects/activitypub_relay/src/python/relay/views.py b/projects/activitypub_relay/src/python/relay/views.py index 8352209..5f11990 100644 --- a/projects/activitypub_relay/src/python/relay/views.py +++ b/projects/activitypub_relay/src/python/relay/views.py @@ -65,7 +65,6 @@ async def actor(request): @register_route("POST", "/actor") async def inbox(request): config = request.app.config - database = request.app.database # reject if missing signature header if "signature" not in request.headers: @@ -114,8 +113,9 @@ async def inbox(request): logging.debug(f"Failed to fetch actor: {data.actorid}") return Response.new_error(400, "failed to fetch actor", "json") - # reject if the actor isn't whitelisted while the whiltelist is enabled - elif config.whitelist_enabled and not config.is_whitelisted(data.domain): + # Reject if the actor isn't whitelisted while the whiltelist is enabled + # An exception is made for follow requests, which we want to enqueue not reject out of hand + elif config.whitelist_enabled and not config.is_whitelisted(data.domain) and data["type"] != "Follow": logging.debug( f"Rejected actor for not being in the whitelist: {data.actorid}" ) @@ -140,17 +140,10 @@ async def inbox(request): logging.debug(f"signature validation failed for: {data.actorid}") return Response.new_error(401, "signature check failed", "json") - # reject if activity type isn't 'Follow' and the actor isn't following - if data["type"] != "Follow" and not database.get_inbox(data.domain): - logging.debug( - f"Rejected actor for trying to post while not following: {data.actorid}" - ) - return Response.new_error(401, "access denied", "json") - logging.debug(f">> payload {data}") - await run_processor(request, actor, data, software) - return Response.new(status=202) + resp = await run_processor(request, actor, data, software) + return resp or Response.new(status=202) @register_route("GET", "/.well-known/webfinger")