From 665b7f58476c6a0a84a1eea38d0a016e0d843213 Mon Sep 17 00:00:00 2001 From: Thomas Sileo Date: Tue, 5 Jul 2022 21:09:49 +0200 Subject: [PATCH] Support actors update --- app/activitypub.py | 19 ++++++++++++++++--- app/boxes.py | 30 +++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/app/activitypub.py b/app/activitypub.py index 8bebc26..e9822a2 100644 --- a/app/activitypub.py +++ b/app/activitypub.py @@ -103,7 +103,7 @@ class NotAnObjectError(Exception): self.resp = resp -async def fetch(url: str, params: dict[str, Any] | None = None) -> dict[str, Any]: +async def fetch(url: str, params: dict[str, Any] | None = None) -> RawObject: async with httpx.AsyncClient() as client: resp = await client.get( url, @@ -223,6 +223,19 @@ def get_actor_id(activity: RawObject) -> str: return get_id(activity["actor"]) +async def get_object(activity: RawObject) -> RawObject: + if "object" not in activity: + raise ValueError(f"No object in {activity}") + + raw_activity_object = activity["object"] + if isinstance(raw_activity_object, dict): + return raw_activity_object + elif isinstance(raw_activity_object, str): + return await fetch(raw_activity_object) + else: + raise ValueError(f"Unexpected object {raw_activity_object}") + + def wrap_object(activity: RawObject) -> RawObject: return { "@context": AS_EXTENDED_CTX, @@ -244,8 +257,8 @@ def wrap_object_if_needed(raw_object: RawObject) -> RawObject: def unwrap_activity(activity: RawObject) -> RawObject: - # FIXME(ts): other types to unwrap? - if activity["type"] == "Create": + # FIXME(ts): deprecate this + if activity["type"] in ["Create", "Update"]: unwrapped_object = activity["object"] # Sanity check, ensure the wrapped object actor matches the activity diff --git a/app/boxes.py b/app/boxes.py index a0b0aca..daad780 100644 --- a/app/boxes.py +++ b/app/boxes.py @@ -590,6 +590,34 @@ async def _handle_undo_activity( # commit will be perfomed in save_to_inbox +async def _handle_update_activity( + db_session: AsyncSession, + from_actor: models.Actor, + update_activity: models.InboxObject, +) -> None: + logger.info("Processing Update activity") + wrapped_object = await ap.get_object(update_activity.ap_object) + if wrapped_object["type"] in ap.ACTOR_TYPES: + logger.info("Updating actor") + + updated_actor = RemoteActor(wrapped_object) + if ( + from_actor.ap_id != updated_actor.ap_id + or from_actor.ap_type != updated_actor.ap_type + or from_actor.handle != updated_actor.handle + ): + raise ValueError( + f"Invalid Update activity {from_actor.ap_actor}/" + f"{updated_actor.ap_actor}" + ) + + # Update the actor + from_actor.ap_actor = updated_actor.ap_actor + else: + # TODO(ts): support updating objects + logger.info(f'Cannot update {wrapped_object["type"]}') + + async def _handle_create_activity( db_session: AsyncSession, from_actor: models.Actor, @@ -742,7 +770,7 @@ async def save_to_inbox( if activity_ro.ap_type == "Create": await _handle_create_activity(db_session, actor, inbox_object) elif activity_ro.ap_type == "Update": - pass + await _handle_update_activity(db_session, actor, inbox_object) elif activity_ro.ap_type == "Delete": if relates_to_inbox_object: await _handle_delete_activity(db_session, actor, relates_to_inbox_object)