From a0fed24ceeb7961e9e16045cdca55f3f756edaae Mon Sep 17 00:00:00 2001 From: Nutomic Date: Sat, 12 Nov 2022 13:52:57 +0000 Subject: [PATCH] When announcing incoming activities, keep extra fields (#2550) --- .../lemmy/activities/block/block_user.json | 2 +- .../activities/block/undo_block_user.json | 2 +- .../lemmy/collections/group_outbox.json | 100 +++++++----- .../apub/src/activities/block/block_user.rs | 1 - .../src/activities/block/undo_block_user.rs | 1 - .../apub/src/activities/community/add_mod.rs | 1 - .../apub/src/activities/community/announce.rs | 144 ++++++++++++++---- crates/apub/src/activities/community/mod.rs | 2 +- .../src/activities/community/remove_mod.rs | 1 - .../apub/src/activities/community/report.rs | 1 - .../apub/src/activities/community/update.rs | 1 - .../activities/create_or_update/comment.rs | 1 - .../src/activities/create_or_update/post.rs | 4 +- .../create_or_update/private_message.rs | 1 - crates/apub/src/activities/deletion/delete.rs | 1 - .../src/activities/deletion/undo_delete.rs | 1 - .../apub/src/activities/following/accept.rs | 1 - .../apub/src/activities/following/follow.rs | 1 - .../src/activities/following/undo_follow.rs | 1 - .../apub/src/activities/voting/undo_vote.rs | 1 - crates/apub/src/activities/voting/vote.rs | 1 - crates/apub/src/activity_lists.rs | 104 +++---------- .../apub/src/collections/community_outbox.rs | 20 ++- crates/apub/src/http/person.rs | 4 +- .../protocol/activities/block/block_user.rs | 4 +- .../activities/block/undo_block_user.rs | 8 +- .../protocol/activities/community/add_mod.rs | 5 +- .../protocol/activities/community/announce.rs | 22 +-- .../activities/community/remove_mod.rs | 5 +- .../protocol/activities/community/report.rs | 4 - .../protocol/activities/community/update.rs | 8 +- .../activities/create_or_update/comment.rs | 5 +- .../activities/create_or_update/post.rs | 5 +- .../create_or_update/private_message.rs | 5 +- .../protocol/activities/deletion/delete.rs | 4 +- .../activities/deletion/undo_delete.rs | 7 +- .../protocol/activities/following/accept.rs | 5 +- .../protocol/activities/following/follow.rs | 8 +- .../activities/following/undo_follow.rs | 5 +- .../protocol/activities/voting/undo_vote.rs | 8 +- .../src/protocol/activities/voting/vote.rs | 9 +- 41 files changed, 243 insertions(+), 271 deletions(-) diff --git a/crates/apub/assets/lemmy/activities/block/block_user.json b/crates/apub/assets/lemmy/activities/block/block_user.json index a12b68e7..f400fa50 100644 --- a/crates/apub/assets/lemmy/activities/block/block_user.json +++ b/crates/apub/assets/lemmy/activities/block/block_user.json @@ -9,7 +9,7 @@ ], "target": "http://enterprise.lemmy.ml/c/main", "type": "Block", - "remove_data": "true", + "removeData": true, "summary": "spam post", "expires": "2021-11-01T12:23:50.151874+00:00", "id": "http://enterprise.lemmy.ml/activities/block/5d42fffb-0903-4625-86d4-0b39bb344fc2" diff --git a/crates/apub/assets/lemmy/activities/block/undo_block_user.json b/crates/apub/assets/lemmy/activities/block/undo_block_user.json index 41c9aad7..99f90778 100644 --- a/crates/apub/assets/lemmy/activities/block/undo_block_user.json +++ b/crates/apub/assets/lemmy/activities/block/undo_block_user.json @@ -14,7 +14,7 @@ ], "target": "http://enterprise.lemmy.ml/c/main", "type": "Block", - "remove_data": "true", + "removeData": true, "summary": "spam post", "expires": "2021-11-01T12:23:50.151874+00:00", "id": "http://enterprise.lemmy.ml/activities/block/726f43ab-bd0e-4ab3-89c8-627e976f553c" diff --git a/crates/apub/assets/lemmy/collections/group_outbox.json b/crates/apub/assets/lemmy/collections/group_outbox.json index 14789177..d39c12d9 100644 --- a/crates/apub/assets/lemmy/collections/group_outbox.json +++ b/crates/apub/assets/lemmy/collections/group_outbox.json @@ -1,61 +1,81 @@ { - "type": "OrderedCollection", - "id": "https://ds9.lemmy.ml/c/testcom/outbox", - "totalItems": 2, - "orderedItems": [ + "type":"OrderedCollection", + "id":"https://ds9.lemmy.ml/c/testcom/outbox", + "totalItems":2, + "orderedItems":[ { - "actor": "https://ds9.lemmy.ml/c/testcom", - "to": [ + "actor":"https://ds9.lemmy.ml/c/testcom", + "to":[ "https://www.w3.org/ns/activitystreams#Public" ], - "object": { - "type": "Page", - "id": "https://ds9.lemmy.ml/post/2328", - "attributedTo": "https://ds9.lemmy.ml/u/nutomic", - "to": [ - "https://ds9.lemmy.ml/c/testcom", + "object":{ + "actor":"https://ds9.lemmy.ml/u/nutomic", + "to":[ "https://www.w3.org/ns/activitystreams#Public" ], - "cc": [], - "name": "another outbox test", - "mediaType": "text/html", - "commentsEnabled": true, - "sensitive": false, - "stickied": false, - "published": "2021-11-18T17:19:45.895163+00:00" + "cc":[ + "https://ds9.lemmy.ml/c/testcom" + ], + "type":"Create", + "id":"http://ds9.lemmy.ml/activities/create/eee6a57a-622f-464d-b560-73ae1fcd3ddf", + "object":{ + "type":"Page", + "id":"https://ds9.lemmy.ml/post/2328", + "attributedTo":"https://ds9.lemmy.ml/u/nutomic", + "to":[ + "https://ds9.lemmy.ml/c/testcom", + "https://www.w3.org/ns/activitystreams#Public" + ], + "name":"another outbox test", + "mediaType":"text/html", + "commentsEnabled":true, + "sensitive":false, + "stickied":false, + "published":"2021-11-18T17:19:45.895163+00:00" + } }, - "cc": [ + "cc":[ "https://ds9.lemmy.ml/c/testcom/followers" ], - "type": "Announce", - "id": "https://ds9.lemmy.ml/activities/announce/b204fe9f-b13d-4af2-9d22-239ac2d892e6" + "type":"Announce", + "id":"https://ds9.lemmy.ml/activities/announce/b204fe9f-b13d-4af2-9d22-239ac2d892e6" }, { - "actor": "https://ds9.lemmy.ml/c/testcom", - "to": [ + "actor":"https://ds9.lemmy.ml/c/testcom", + "to":[ "https://www.w3.org/ns/activitystreams#Public" ], - "object": { - "type": "Page", - "id": "https://ds9.lemmy.ml/post/2327", - "attributedTo": "https://ds9.lemmy.ml/u/nutomic", - "to": [ - "https://ds9.lemmy.ml/c/testcom", + "object":{ + "actor":"https://ds9.lemmy.ml/u/nutomic", + "to":[ "https://www.w3.org/ns/activitystreams#Public" ], - "cc": [], - "name": "outbox test", - "mediaType": "text/html", - "commentsEnabled": true, - "sensitive": false, - "stickied": false, - "published": "2021-11-18T17:19:05.763109+00:00" + "cc":[ + "https://ds9.lemmy.ml/c/testcom" + ], + "type":"Create", + "id":"http://ds9.lemmy.ml/activities/create/eee6a57a-622f-464d-b560-73ae1fcd3ddf", + "object":{ + "type":"Page", + "id":"https://ds9.lemmy.ml/post/2327", + "attributedTo":"https://ds9.lemmy.ml/u/nutomic", + "to":[ + "https://ds9.lemmy.ml/c/testcom", + "https://www.w3.org/ns/activitystreams#Public" + ], + "name":"outbox test", + "mediaType":"text/html", + "commentsEnabled":true, + "sensitive":false, + "stickied":false, + "published":"2021-11-18T17:19:05.763109+00:00" + } }, - "cc": [ + "cc":[ "https://ds9.lemmy.ml/c/testcom/followers" ], - "type": "Announce", - "id": "https://ds9.lemmy.ml/activities/announce/c6c960ce-c8d8-4231-925e-3ba367468f18" + "type":"Announce", + "id":"https://ds9.lemmy.ml/activities/announce/c6c960ce-c8d8-4231-925e-3ba367468f18" } ] } \ No newline at end of file diff --git a/crates/apub/src/activities/block/block_user.rs b/crates/apub/src/activities/block/block_user.rs index 54084707..f27ec27d 100644 --- a/crates/apub/src/activities/block/block_user.rs +++ b/crates/apub/src/activities/block/block_user.rs @@ -65,7 +65,6 @@ impl BlockUser { &context.settings().get_protocol_and_hostname(), )?, expires: expires.map(convert_datetime), - unparsed: Default::default(), }) } diff --git a/crates/apub/src/activities/block/undo_block_user.rs b/crates/apub/src/activities/block/undo_block_user.rs index f18516f9..bf177ae8 100644 --- a/crates/apub/src/activities/block/undo_block_user.rs +++ b/crates/apub/src/activities/block/undo_block_user.rs @@ -53,7 +53,6 @@ impl UndoBlockUser { cc: generate_cc(target, context.pool()).await?, kind: UndoType::Undo, id: id.clone(), - unparsed: Default::default(), }; let mut inboxes = vec![user.shared_inbox_or_inbox()]; diff --git a/crates/apub/src/activities/community/add_mod.rs b/crates/apub/src/activities/community/add_mod.rs index ba78ec3b..6d7f9633 100644 --- a/crates/apub/src/activities/community/add_mod.rs +++ b/crates/apub/src/activities/community/add_mod.rs @@ -55,7 +55,6 @@ impl AddMod { cc: vec![community.actor_id()], kind: AddType::Add, id: id.clone(), - unparsed: Default::default(), }; let activity = AnnouncableActivities::AddMod(add); diff --git a/crates/apub/src/activities/community/announce.rs b/crates/apub/src/activities/community/announce.rs index e5419680..f99a650f 100644 --- a/crates/apub/src/activities/community/announce.rs +++ b/crates/apub/src/activities/community/announce.rs @@ -1,10 +1,16 @@ use crate::{ - activities::{generate_activity_id, send_lemmy_activity, verify_is_public}, + activities::{ + generate_activity_id, + send_lemmy_activity, + verify_is_public, + verify_person_in_community, + }, activity_lists::AnnouncableActivities, insert_activity, objects::community::ApubCommunity, protocol::{ - activities::{community::announce::AnnounceActivity, CreateOrUpdateType}, + activities::community::announce::{AnnounceActivity, RawAnnouncableActivities}, + Id, IdOrNestedObject, }, ActorType, @@ -13,9 +19,59 @@ use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::Acti use activitystreams_kinds::{activity::AnnounceType, public}; use lemmy_utils::error::LemmyError; use lemmy_websocket::LemmyContext; +use serde_json::Value; use tracing::debug; use url::Url; +#[async_trait::async_trait(?Send)] +impl ActivityHandler for RawAnnouncableActivities { + type DataType = LemmyContext; + type Error = LemmyError; + + fn id(&self) -> &Url { + &self.id + } + + fn actor(&self) -> &Url { + &self.actor + } + + #[tracing::instrument(skip_all)] + async fn verify( + &self, + _data: &Data, + _request_counter: &mut i32, + ) -> Result<(), Self::Error> { + Ok(()) + } + + #[tracing::instrument(skip_all)] + async fn receive( + self, + data: &Data, + request_counter: &mut i32, + ) -> Result<(), Self::Error> { + let activity: AnnouncableActivities = self.clone().try_into()?; + // This is only for sending, not receiving so we reject it. + if let AnnouncableActivities::Page(_) = activity { + return Err(LemmyError::from_message("Cant receive page")); + } + let community = activity.get_community(data, &mut 0).await?; + let actor_id = ObjectId::new(activity.actor().clone()); + + // verify and receive activity + activity.verify(data, request_counter).await?; + activity.receive(data, request_counter).await?; + + // send to community followers + if community.local { + verify_person_in_community(&actor_id, &community, data, &mut 0).await?; + AnnounceActivity::send(self, &community, data).await?; + } + Ok(()) + } +} + #[async_trait::async_trait(?Send)] pub(crate) trait GetCommunity { async fn get_community( @@ -27,7 +83,7 @@ pub(crate) trait GetCommunity { impl AnnounceActivity { pub(crate) fn new( - object: AnnouncableActivities, + object: RawAnnouncableActivities, community: &ApubCommunity, context: &LemmyContext, ) -> Result { @@ -41,13 +97,12 @@ impl AnnounceActivity { &AnnounceType::Announce, &context.settings().get_protocol_and_hostname(), )?, - unparsed: Default::default(), }) } #[tracing::instrument(skip_all)] pub async fn send( - object: AnnouncableActivities, + object: RawAnnouncableActivities, community: &ApubCommunity, context: &LemmyContext, ) -> Result<(), LemmyError> { @@ -57,13 +112,18 @@ impl AnnounceActivity { // Pleroma and Mastodon can't handle activities like Announce/Create/Page. So for // compatibility, we also send Announce/Page so that they can follow Lemmy communities. - use AnnouncableActivities::*; - let object = match object { - CreateOrUpdatePost(c) if c.kind == CreateOrUpdateType::Create => Page(c.object), - _ => return Ok(()), - }; - let announce_compat = AnnounceActivity::new(object, community, context)?; - send_lemmy_activity(context, announce_compat, community, inboxes, false).await?; + let object_parsed = object.try_into()?; + if let AnnouncableActivities::CreateOrUpdatePost(c) = object_parsed { + // Hack: need to convert Page into a format which can be sent as activity, which requires + // adding actor field. + let announcable_page = RawAnnouncableActivities { + id: c.object.id.clone().into_inner(), + actor: c.actor.clone().into_inner(), + other: serde_json::to_value(c.object)?.as_object().unwrap().clone(), + }; + let announce_compat = AnnounceActivity::new(announcable_page, community, context)?; + send_lemmy_activity(context, announce_compat, community, inboxes, false).await?; + } Ok(()) } } @@ -97,27 +157,53 @@ impl ActivityHandler for AnnounceActivity { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let object = self.object.object(context, request_counter).await?; + let object: AnnouncableActivities = self + .object + .object(context, request_counter) + .await? + .try_into()?; + // This is only for sending, not receiving so we reject it. + if let AnnouncableActivities::Page(_) = object { + return Err(LemmyError::from_message("Cant receive page")); + } + // we have to verify this here in order to avoid fetching the object twice over http object.verify(context, request_counter).await?; - // TODO: this can probably be implemented in a cleaner way - match object { - // Dont insert these into activities table, as they are not activities. - AnnouncableActivities::Page(_) => {} - _ => { - let object_value = serde_json::to_value(&object)?; - let insert = - insert_activity(object.id(), object_value, false, true, context.pool()).await?; - if !insert { - debug!( - "Received duplicate activity in announce {}", - object.id().to_string() - ); - return Ok(()); - } - } + let object_value = serde_json::to_value(&object)?; + let insert = insert_activity(object.id(), object_value, false, true, context.pool()).await?; + if !insert { + debug!( + "Received duplicate activity in announce {}", + object.id().to_string() + ); + return Ok(()); } object.receive(context, request_counter).await } } + +impl Id for RawAnnouncableActivities { + fn object_id(&self) -> &Url { + ActivityHandler::id(self) + } +} + +impl TryFrom for AnnouncableActivities { + type Error = serde_json::error::Error; + + fn try_from(value: RawAnnouncableActivities) -> Result { + let mut map = value.other.clone(); + map.insert("id".to_string(), Value::String(value.id.to_string())); + map.insert("actor".to_string(), Value::String(value.actor.to_string())); + serde_json::from_value(Value::Object(map)) + } +} + +impl TryFrom for RawAnnouncableActivities { + type Error = serde_json::error::Error; + + fn try_from(value: AnnouncableActivities) -> Result { + serde_json::from_value(serde_json::to_value(value)?) + } +} diff --git a/crates/apub/src/activities/community/mod.rs b/crates/apub/src/activities/community/mod.rs index 4d96ca4c..aed7ddb9 100644 --- a/crates/apub/src/activities/community/mod.rs +++ b/crates/apub/src/activities/community/mod.rs @@ -32,7 +32,7 @@ where send_lemmy_activity(context, activity.clone(), actor, inboxes, false).await?; if community.local { - AnnounceActivity::send(activity, community, context).await?; + AnnounceActivity::send(activity.try_into()?, community, context).await?; } Ok(()) diff --git a/crates/apub/src/activities/community/remove_mod.rs b/crates/apub/src/activities/community/remove_mod.rs index 82c499e4..2f2419f5 100644 --- a/crates/apub/src/activities/community/remove_mod.rs +++ b/crates/apub/src/activities/community/remove_mod.rs @@ -55,7 +55,6 @@ impl RemoveMod { id: id.clone(), cc: vec![community.actor_id()], kind: RemoveType::Remove, - unparsed: Default::default(), }; let activity = AnnouncableActivities::RemoveMod(remove); diff --git a/crates/apub/src/activities/community/report.rs b/crates/apub/src/activities/community/report.rs index 33322bf3..867307f5 100644 --- a/crates/apub/src/activities/community/report.rs +++ b/crates/apub/src/activities/community/report.rs @@ -47,7 +47,6 @@ impl Report { summary: reason, kind, id: id.clone(), - unparsed: Default::default(), }; let inbox = vec![community.shared_inbox_or_inbox()]; diff --git a/crates/apub/src/activities/community/update.rs b/crates/apub/src/activities/community/update.rs index f19aafec..a9134d47 100644 --- a/crates/apub/src/activities/community/update.rs +++ b/crates/apub/src/activities/community/update.rs @@ -41,7 +41,6 @@ impl UpdateCommunity { cc: vec![community.actor_id()], kind: UpdateType::Update, id: id.clone(), - unparsed: Default::default(), }; let activity = AnnouncableActivities::UpdateCommunity(update); diff --git a/crates/apub/src/activities/create_or_update/comment.rs b/crates/apub/src/activities/create_or_update/comment.rs index 1c6ce511..e367bec9 100644 --- a/crates/apub/src/activities/create_or_update/comment.rs +++ b/crates/apub/src/activities/create_or_update/comment.rs @@ -63,7 +63,6 @@ impl CreateOrUpdateComment { object: note, kind, id: id.clone(), - unparsed: Default::default(), }; let tagged_users: Vec> = create_or_update diff --git a/crates/apub/src/activities/create_or_update/post.rs b/crates/apub/src/activities/create_or_update/post.rs index 09e9ae0d..4470f3b3 100644 --- a/crates/apub/src/activities/create_or_update/post.rs +++ b/crates/apub/src/activities/create_or_update/post.rs @@ -49,7 +49,6 @@ impl CreateOrUpdatePost { cc: vec![community.actor_id()], kind, id: id.clone(), - unparsed: Default::default(), }) } @@ -65,7 +64,8 @@ impl CreateOrUpdatePost { let create_or_update = CreateOrUpdatePost::new(post, actor, &community, kind, context).await?; let activity = AnnouncableActivities::CreateOrUpdatePost(Box::new(create_or_update)); - send_activity_in_community(activity, actor, &community, vec![], context).await + send_activity_in_community(activity, actor, &community, vec![], context).await?; + Ok(()) } } diff --git a/crates/apub/src/activities/create_or_update/private_message.rs b/crates/apub/src/activities/create_or_update/private_message.rs index 523135db..e0766196 100644 --- a/crates/apub/src/activities/create_or_update/private_message.rs +++ b/crates/apub/src/activities/create_or_update/private_message.rs @@ -39,7 +39,6 @@ impl CreateOrUpdatePrivateMessage { to: [ObjectId::new(recipient.actor_id())], object: private_message.into_apub(context).await?, kind, - unparsed: Default::default(), }; let inbox = vec![recipient.shared_inbox_or_inbox()]; send_lemmy_activity(context, create_or_update, actor, inbox, true).await diff --git a/crates/apub/src/activities/deletion/delete.rs b/crates/apub/src/activities/deletion/delete.rs index 0124f233..8d296332 100644 --- a/crates/apub/src/activities/deletion/delete.rs +++ b/crates/apub/src/activities/deletion/delete.rs @@ -117,7 +117,6 @@ impl Delete { kind: DeleteType::Delete, summary, id, - unparsed: Default::default(), }) } } diff --git a/crates/apub/src/activities/deletion/undo_delete.rs b/crates/apub/src/activities/deletion/undo_delete.rs index 29f75e6d..c3533ad5 100644 --- a/crates/apub/src/activities/deletion/undo_delete.rs +++ b/crates/apub/src/activities/deletion/undo_delete.rs @@ -117,7 +117,6 @@ impl UndoDelete { cc: cc.into_iter().collect(), kind: UndoType::Undo, id, - unparsed: Default::default(), }) } diff --git a/crates/apub/src/activities/following/accept.rs b/crates/apub/src/activities/following/accept.rs index 55a37449..bda5a0ac 100644 --- a/crates/apub/src/activities/following/accept.rs +++ b/crates/apub/src/activities/following/accept.rs @@ -40,7 +40,6 @@ impl AcceptFollowCommunity { AcceptType::Accept, &context.settings().get_protocol_and_hostname(), )?, - unparsed: Default::default(), }; let inbox = vec![person.shared_inbox_or_inbox()]; send_lemmy_activity(context, accept, &community, inbox, true).await diff --git a/crates/apub/src/activities/following/follow.rs b/crates/apub/src/activities/following/follow.rs index 57390b7f..ad441091 100644 --- a/crates/apub/src/activities/following/follow.rs +++ b/crates/apub/src/activities/following/follow.rs @@ -38,7 +38,6 @@ impl FollowCommunity { FollowType::Follow, &context.settings().get_protocol_and_hostname(), )?, - unparsed: Default::default(), }) } diff --git a/crates/apub/src/activities/following/undo_follow.rs b/crates/apub/src/activities/following/undo_follow.rs index 221d8184..b90039cd 100644 --- a/crates/apub/src/activities/following/undo_follow.rs +++ b/crates/apub/src/activities/following/undo_follow.rs @@ -36,7 +36,6 @@ impl UndoFollowCommunity { UndoType::Undo, &context.settings().get_protocol_and_hostname(), )?, - unparsed: Default::default(), }; let inbox = vec![community.shared_inbox_or_inbox()]; send_lemmy_activity(context, undo, actor, inbox, true).await diff --git a/crates/apub/src/activities/voting/undo_vote.rs b/crates/apub/src/activities/voting/undo_vote.rs index 9415e547..8980e71f 100644 --- a/crates/apub/src/activities/voting/undo_vote.rs +++ b/crates/apub/src/activities/voting/undo_vote.rs @@ -51,7 +51,6 @@ impl UndoVote { object, kind: UndoType::Undo, id: id.clone(), - unparsed: Default::default(), }; let activity = AnnouncableActivities::UndoVote(undo_vote); send_activity_in_community(activity, actor, &community, vec![], context).await diff --git a/crates/apub/src/activities/voting/vote.rs b/crates/apub/src/activities/voting/vote.rs index 8a022f7d..6608ad9c 100644 --- a/crates/apub/src/activities/voting/vote.rs +++ b/crates/apub/src/activities/voting/vote.rs @@ -37,7 +37,6 @@ impl Vote { object: ObjectId::new(object.ap_id()), kind: kind.clone(), id: generate_activity_id(kind, &context.settings().get_protocol_and_hostname())?, - unparsed: Default::default(), }) } diff --git a/crates/apub/src/activity_lists.rs b/crates/apub/src/activity_lists.rs index dbd185bd..86e87a2a 100644 --- a/crates/apub/src/activity_lists.rs +++ b/crates/apub/src/activity_lists.rs @@ -1,12 +1,12 @@ use crate::{ - activities::{community::announce::GetCommunity, verify_person_in_community}, + activities::community::announce::GetCommunity, objects::community::ApubCommunity, protocol::{ activities::{ block::{block_user::BlockUser, undo_block_user::UndoBlockUser}, community::{ add_mod::AddMod, - announce::AnnounceActivity, + announce::{AnnounceActivity, RawAnnouncableActivities}, remove_mod::RemoveMod, report::Report, update::UpdateCommunity, @@ -25,15 +25,9 @@ use crate::{ voting::{undo_vote::UndoVote, vote::Vote}, }, objects::page::Page, - Id, }, }; -use activitypub_federation::{ - core::object_id::ObjectId, - data::Data, - deser::context::WithContext, - traits::{activity_handler, ActivityHandler}, -}; +use activitypub_federation::{deser::context::WithContext, traits::activity_handler}; use lemmy_utils::error::LemmyError; use lemmy_websocket::LemmyContext; use serde::{Deserialize, Serialize}; @@ -43,19 +37,19 @@ use url::Url; #[serde(untagged)] #[activity_handler(LemmyContext, LemmyError)] pub enum SharedInboxActivities { - GroupInboxActivities(Box>), - // Note, pm activities need to be at the end, otherwise comments will end up here. We can probably - // avoid this problem by replacing createpm.object with our own struct, instead of NoteExt. PersonInboxActivities(Box>), + GroupInboxActivities(Box>), } #[derive(Debug, Deserialize, Serialize)] #[serde(untagged)] +#[activity_handler(LemmyContext, LemmyError)] pub enum GroupInboxActivities { FollowCommunity(FollowCommunity), UndoFollowCommunity(UndoFollowCommunity), - AnnouncableActivities(Box), Report(Report), + // This is a catch-all and needs to be last + AnnouncableActivities(RawAnnouncableActivities), } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -63,14 +57,23 @@ pub enum GroupInboxActivities { #[activity_handler(LemmyContext, LemmyError)] pub enum PersonInboxActivities { AcceptFollowCommunity(AcceptFollowCommunity), - /// Some activities can also be sent from user to user, eg a comment with mentions - AnnouncableActivities(AnnouncableActivities), CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage), Delete(Delete), UndoDelete(UndoDelete), AnnounceActivity(AnnounceActivity), } +/// This is necessary for user inbox, which can also receive some "announcable" activities, +/// eg a comment mention. This needs to be a separate enum so that announcables received in shared +/// inbox can fall through to be parsed as GroupInboxActivities::AnnouncableActivities. +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(untagged)] +#[activity_handler(LemmyContext, LemmyError)] +pub enum PersonInboxActivitiesWithAnnouncable { + PersonInboxActivities(PersonInboxActivities), + AnnouncableActivities(AnnouncableActivities), +} + #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(untagged)] #[activity_handler(LemmyContext, LemmyError)] @@ -127,77 +130,6 @@ impl GetCommunity for AnnouncableActivities { } } -impl Id for AnnouncableActivities { - fn object_id(&self) -> &Url { - ActivityHandler::id(self) - } -} - -// Need to implement this manually to announce matching activities -#[async_trait::async_trait(?Send)] -impl ActivityHandler for GroupInboxActivities { - type DataType = LemmyContext; - type Error = LemmyError; - - fn id(&self) -> &Url { - match self { - GroupInboxActivities::FollowCommunity(a) => a.id(), - GroupInboxActivities::UndoFollowCommunity(a) => a.id(), - GroupInboxActivities::AnnouncableActivities(a) => a.object_id(), - GroupInboxActivities::Report(a) => a.id(), - } - } - - fn actor(&self) -> &Url { - match self { - GroupInboxActivities::FollowCommunity(a) => a.actor(), - GroupInboxActivities::UndoFollowCommunity(a) => a.actor(), - GroupInboxActivities::AnnouncableActivities(a) => a.actor(), - GroupInboxActivities::Report(a) => a.actor(), - } - } - - async fn verify( - &self, - data: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - match self { - GroupInboxActivities::FollowCommunity(a) => a.verify(data, request_counter).await, - GroupInboxActivities::UndoFollowCommunity(a) => a.verify(data, request_counter).await, - GroupInboxActivities::AnnouncableActivities(a) => a.verify(data, request_counter).await, - GroupInboxActivities::Report(a) => a.verify(data, request_counter).await, - } - } - - async fn receive( - self, - data: &Data, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - match self { - GroupInboxActivities::FollowCommunity(a) => a.receive(data, request_counter).await, - GroupInboxActivities::UndoFollowCommunity(a) => a.receive(data, request_counter).await, - GroupInboxActivities::AnnouncableActivities(activity) => { - activity.clone().receive(data, request_counter).await?; - - // Ignore failures in get_community(). those happen because Delete/PrivateMessage is not in a - // community, but looks identical to Delete/Post or Delete/Comment which are in a community. - let community = activity.get_community(data, &mut 0).await; - if let Ok(community) = community { - if community.local { - let actor_id = ObjectId::new(activity.actor().clone()); - verify_person_in_community(&actor_id, &community, data, &mut 0).await?; - AnnounceActivity::send(*activity, &community, data).await?; - } - } - Ok(()) - } - GroupInboxActivities::Report(a) => a.receive(data, request_counter).await, - } - } -} - #[cfg(test)] mod tests { use crate::{ diff --git a/crates/apub/src/collections/community_outbox.rs b/crates/apub/src/collections/community_outbox.rs index d9fbf4f2..57fc48be 100644 --- a/crates/apub/src/collections/community_outbox.rs +++ b/crates/apub/src/collections/community_outbox.rs @@ -4,7 +4,11 @@ use crate::{ generate_outbox_url, objects::post::ApubPost, protocol::{ - activities::community::announce::AnnounceActivity, + activities::{ + community::announce::AnnounceActivity, + create_or_update::post::CreateOrUpdatePost, + CreateOrUpdateType, + }, collections::group_outbox::GroupOutbox, }, }; @@ -16,7 +20,10 @@ use activitypub_federation::{ use activitystreams_kinds::collection::OrderedCollectionType; use chrono::NaiveDateTime; use futures::future::join_all; -use lemmy_db_schema::source::post::Post; +use lemmy_db_schema::{ + source::{person::Person, post::Post}, + traits::Crud, +}; use lemmy_utils::error::LemmyError; use url::Url; @@ -61,9 +68,12 @@ impl ApubObject for ApubCommunityOutbox { async fn into_apub(self, data: &Self::DataType) -> Result { let mut ordered_items = vec![]; for post in self.0 { - let page = post.into_apub(&data.1).await?; - let announcable = AnnouncableActivities::Page(page); - let announce = AnnounceActivity::new(announcable, &data.0, &data.1)?; + let person = Person::read(data.1.pool(), post.creator_id).await?.into(); + let create = + CreateOrUpdatePost::new(post, &person, &data.0, CreateOrUpdateType::Create, &data.1) + .await?; + let announcable = AnnouncableActivities::CreateOrUpdatePost(Box::new(create)); + let announce = AnnounceActivity::new(announcable.try_into()?, &data.0, &data.1)?; ordered_items.push(announce); } diff --git a/crates/apub/src/http/person.rs b/crates/apub/src/http/person.rs index bf82a47c..1653697b 100644 --- a/crates/apub/src/http/person.rs +++ b/crates/apub/src/http/person.rs @@ -1,5 +1,5 @@ use crate::{ - activity_lists::PersonInboxActivities, + activity_lists::PersonInboxActivitiesWithAnnouncable, fetcher::user_or_community::UserOrCommunity, generate_outbox_url, http::{create_apub_response, create_apub_tombstone_response, receive_lemmy_activity}, @@ -45,7 +45,7 @@ pub async fn person_inbox( payload: String, context: web::Data, ) -> Result { - receive_lemmy_activity::, UserOrCommunity>( + receive_lemmy_activity::, UserOrCommunity>( request, payload, context, ) .await diff --git a/crates/apub/src/protocol/activities/block/block_user.rs b/crates/apub/src/protocol/activities/block/block_user.rs index 1929a6a5..3925c55c 100644 --- a/crates/apub/src/protocol/activities/block/block_user.rs +++ b/crates/apub/src/protocol/activities/block/block_user.rs @@ -1,4 +1,4 @@ -use crate::{activities::block::SiteOrCommunity, objects::person::ApubPerson, protocol::Unparsed}; +use crate::{activities::block::SiteOrCommunity, objects::person::ApubPerson}; use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many}; use activitystreams_kinds::activity::BlockType; use chrono::{DateTime, FixedOffset}; @@ -25,6 +25,4 @@ pub struct BlockUser { /// block reason, written to mod log pub(crate) summary: Option, pub(crate) expires: Option>, - #[serde(flatten)] - pub(crate) unparsed: Unparsed, } diff --git a/crates/apub/src/protocol/activities/block/undo_block_user.rs b/crates/apub/src/protocol/activities/block/undo_block_user.rs index b2dcfaff..df161143 100644 --- a/crates/apub/src/protocol/activities/block/undo_block_user.rs +++ b/crates/apub/src/protocol/activities/block/undo_block_user.rs @@ -1,7 +1,4 @@ -use crate::{ - objects::person::ApubPerson, - protocol::{activities::block::block_user::BlockUser, Unparsed}, -}; +use crate::{objects::person::ApubPerson, protocol::activities::block::block_user::BlockUser}; use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many}; use activitystreams_kinds::activity::UndoType; use serde::{Deserialize, Serialize}; @@ -19,7 +16,4 @@ pub struct UndoBlockUser { #[serde(rename = "type")] pub(crate) kind: UndoType, pub(crate) id: Url, - - #[serde(flatten)] - pub(crate) unparsed: Unparsed, } diff --git a/crates/apub/src/protocol/activities/community/add_mod.rs b/crates/apub/src/protocol/activities/community/add_mod.rs index e720b8e6..b9ce2420 100644 --- a/crates/apub/src/protocol/activities/community/add_mod.rs +++ b/crates/apub/src/protocol/activities/community/add_mod.rs @@ -1,4 +1,4 @@ -use crate::{objects::person::ApubPerson, protocol::Unparsed}; +use crate::objects::person::ApubPerson; use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many}; use activitystreams_kinds::activity::AddType; use serde::{Deserialize, Serialize}; @@ -17,7 +17,4 @@ pub struct AddMod { #[serde(rename = "type")] pub(crate) kind: AddType, pub(crate) id: Url, - - #[serde(flatten)] - pub(crate) unparsed: Unparsed, } diff --git a/crates/apub/src/protocol/activities/community/announce.rs b/crates/apub/src/protocol/activities/community/announce.rs index 9db17a0f..7c2e16ec 100644 --- a/crates/apub/src/protocol/activities/community/announce.rs +++ b/crates/apub/src/protocol/activities/community/announce.rs @@ -1,11 +1,8 @@ -use crate::{ - activity_lists::AnnouncableActivities, - objects::community::ApubCommunity, - protocol::{IdOrNestedObject, Unparsed}, -}; +use crate::{objects::community::ApubCommunity, protocol::IdOrNestedObject}; use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many}; use activitystreams_kinds::activity::AnnounceType; use serde::{Deserialize, Serialize}; +use serde_json::{Map, Value}; use url::Url; #[derive(Clone, Debug, Deserialize, Serialize)] @@ -14,13 +11,20 @@ pub struct AnnounceActivity { pub(crate) actor: ObjectId, #[serde(deserialize_with = "deserialize_one_or_many")] pub(crate) to: Vec, - pub(crate) object: IdOrNestedObject, + pub(crate) object: IdOrNestedObject, #[serde(deserialize_with = "deserialize_one_or_many")] pub(crate) cc: Vec, #[serde(rename = "type")] pub(crate) kind: AnnounceType, pub(crate) id: Url, - - #[serde(flatten)] - pub(crate) unparsed: Unparsed, +} + +/// Use this to receive community inbox activities, and then announce them if valid. This +/// ensures that all json fields are kept, even if Lemmy doesnt understand them. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct RawAnnouncableActivities { + pub(crate) id: Url, + pub(crate) actor: Url, + #[serde(flatten)] + pub(crate) other: Map, } diff --git a/crates/apub/src/protocol/activities/community/remove_mod.rs b/crates/apub/src/protocol/activities/community/remove_mod.rs index bd972202..6fd20af4 100644 --- a/crates/apub/src/protocol/activities/community/remove_mod.rs +++ b/crates/apub/src/protocol/activities/community/remove_mod.rs @@ -1,4 +1,4 @@ -use crate::{objects::person::ApubPerson, protocol::Unparsed}; +use crate::objects::person::ApubPerson; use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many}; use activitystreams_kinds::activity::RemoveType; use serde::{Deserialize, Serialize}; @@ -17,7 +17,4 @@ pub struct RemoveMod { pub(crate) kind: RemoveType, pub(crate) target: Url, pub(crate) id: Url, - - #[serde(flatten)] - pub(crate) unparsed: Unparsed, } diff --git a/crates/apub/src/protocol/activities/community/report.rs b/crates/apub/src/protocol/activities/community/report.rs index 4c2649ee..f9830c85 100644 --- a/crates/apub/src/protocol/activities/community/report.rs +++ b/crates/apub/src/protocol/activities/community/report.rs @@ -1,7 +1,6 @@ use crate::{ fetcher::post_or_comment::PostOrComment, objects::{community::ApubCommunity, person::ApubPerson}, - protocol::Unparsed, }; use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one}; use activitystreams_kinds::activity::FlagType; @@ -19,7 +18,4 @@ pub struct Report { #[serde(rename = "type")] pub(crate) kind: FlagType, pub(crate) id: Url, - - #[serde(flatten)] - pub(crate) unparsed: Unparsed, } diff --git a/crates/apub/src/protocol/activities/community/update.rs b/crates/apub/src/protocol/activities/community/update.rs index d0b27a55..f6ef99c0 100644 --- a/crates/apub/src/protocol/activities/community/update.rs +++ b/crates/apub/src/protocol/activities/community/update.rs @@ -1,7 +1,4 @@ -use crate::{ - objects::person::ApubPerson, - protocol::{objects::group::Group, Unparsed}, -}; +use crate::{objects::person::ApubPerson, protocol::objects::group::Group}; use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many}; use activitystreams_kinds::activity::UpdateType; use serde::{Deserialize, Serialize}; @@ -22,7 +19,4 @@ pub struct UpdateCommunity { #[serde(rename = "type")] pub(crate) kind: UpdateType, pub(crate) id: Url, - - #[serde(flatten)] - pub(crate) unparsed: Unparsed, } diff --git a/crates/apub/src/protocol/activities/create_or_update/comment.rs b/crates/apub/src/protocol/activities/create_or_update/comment.rs index 5ebc0f55..3049b79c 100644 --- a/crates/apub/src/protocol/activities/create_or_update/comment.rs +++ b/crates/apub/src/protocol/activities/create_or_update/comment.rs @@ -1,7 +1,7 @@ use crate::{ mentions::MentionOrValue, objects::person::ApubPerson, - protocol::{activities::CreateOrUpdateType, objects::note::Note, Unparsed}, + protocol::{activities::CreateOrUpdateType, objects::note::Note}, }; use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many}; use serde::{Deserialize, Serialize}; @@ -21,7 +21,4 @@ pub struct CreateOrUpdateComment { #[serde(rename = "type")] pub(crate) kind: CreateOrUpdateType, pub(crate) id: Url, - - #[serde(flatten)] - pub(crate) unparsed: Unparsed, } diff --git a/crates/apub/src/protocol/activities/create_or_update/post.rs b/crates/apub/src/protocol/activities/create_or_update/post.rs index d40073cf..70b15677 100644 --- a/crates/apub/src/protocol/activities/create_or_update/post.rs +++ b/crates/apub/src/protocol/activities/create_or_update/post.rs @@ -1,6 +1,6 @@ use crate::{ objects::person::ApubPerson, - protocol::{activities::CreateOrUpdateType, objects::page::Page, Unparsed}, + protocol::{activities::CreateOrUpdateType, objects::page::Page}, }; use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many}; use serde::{Deserialize, Serialize}; @@ -18,7 +18,4 @@ pub struct CreateOrUpdatePost { #[serde(rename = "type")] pub(crate) kind: CreateOrUpdateType, pub(crate) id: Url, - - #[serde(flatten)] - pub(crate) unparsed: Unparsed, } diff --git a/crates/apub/src/protocol/activities/create_or_update/private_message.rs b/crates/apub/src/protocol/activities/create_or_update/private_message.rs index 9cee8ddf..6a9b585b 100644 --- a/crates/apub/src/protocol/activities/create_or_update/private_message.rs +++ b/crates/apub/src/protocol/activities/create_or_update/private_message.rs @@ -1,6 +1,6 @@ use crate::{ objects::person::ApubPerson, - protocol::{activities::CreateOrUpdateType, objects::chat_message::ChatMessage, Unparsed}, + protocol::{activities::CreateOrUpdateType, objects::chat_message::ChatMessage}, }; use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one}; use serde::{Deserialize, Serialize}; @@ -16,7 +16,4 @@ pub struct CreateOrUpdatePrivateMessage { pub(crate) object: ChatMessage, #[serde(rename = "type")] pub(crate) kind: CreateOrUpdateType, - - #[serde(flatten)] - pub(crate) unparsed: Unparsed, } diff --git a/crates/apub/src/protocol/activities/deletion/delete.rs b/crates/apub/src/protocol/activities/deletion/delete.rs index 76256cac..d3dcaf57 100644 --- a/crates/apub/src/protocol/activities/deletion/delete.rs +++ b/crates/apub/src/protocol/activities/deletion/delete.rs @@ -1,6 +1,6 @@ use crate::{ objects::person::ApubPerson, - protocol::{objects::tombstone::Tombstone, IdOrNestedObject, Unparsed}, + protocol::{objects::tombstone::Tombstone, IdOrNestedObject}, }; use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many}; use activitystreams_kinds::activity::DeleteType; @@ -27,6 +27,4 @@ pub struct Delete { /// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user /// deleting their own content. pub(crate) summary: Option, - #[serde(flatten)] - pub(crate) unparsed: Unparsed, } diff --git a/crates/apub/src/protocol/activities/deletion/undo_delete.rs b/crates/apub/src/protocol/activities/deletion/undo_delete.rs index 1ae6aa84..02cbf178 100644 --- a/crates/apub/src/protocol/activities/deletion/undo_delete.rs +++ b/crates/apub/src/protocol/activities/deletion/undo_delete.rs @@ -1,7 +1,4 @@ -use crate::{ - objects::person::ApubPerson, - protocol::{activities::deletion::delete::Delete, Unparsed}, -}; +use crate::{objects::person::ApubPerson, protocol::activities::deletion::delete::Delete}; use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many}; use activitystreams_kinds::activity::UndoType; use serde::{Deserialize, Serialize}; @@ -21,6 +18,4 @@ pub struct UndoDelete { #[serde(deserialize_with = "deserialize_one_or_many", default)] #[serde(skip_serializing_if = "Vec::is_empty")] pub(crate) cc: Vec, - #[serde(flatten)] - pub(crate) unparsed: Unparsed, } diff --git a/crates/apub/src/protocol/activities/following/accept.rs b/crates/apub/src/protocol/activities/following/accept.rs index a600fbfb..ce1a1add 100644 --- a/crates/apub/src/protocol/activities/following/accept.rs +++ b/crates/apub/src/protocol/activities/following/accept.rs @@ -1,6 +1,6 @@ use crate::{ objects::community::ApubCommunity, - protocol::{activities::following::follow::FollowCommunity, Unparsed}, + protocol::activities::following::follow::FollowCommunity, }; use activitypub_federation::core::object_id::ObjectId; use activitystreams_kinds::activity::AcceptType; @@ -15,7 +15,4 @@ pub struct AcceptFollowCommunity { #[serde(rename = "type")] pub(crate) kind: AcceptType, pub(crate) id: Url, - - #[serde(flatten)] - pub(crate) unparsed: Unparsed, } diff --git a/crates/apub/src/protocol/activities/following/follow.rs b/crates/apub/src/protocol/activities/following/follow.rs index 2cef197d..f1501050 100644 --- a/crates/apub/src/protocol/activities/following/follow.rs +++ b/crates/apub/src/protocol/activities/following/follow.rs @@ -1,7 +1,4 @@ -use crate::{ - objects::{community::ApubCommunity, person::ApubPerson}, - protocol::Unparsed, -}; +use crate::objects::{community::ApubCommunity, person::ApubPerson}; use activitypub_federation::core::object_id::ObjectId; use activitystreams_kinds::activity::FollowType; use serde::{Deserialize, Serialize}; @@ -15,7 +12,4 @@ pub struct FollowCommunity { #[serde(rename = "type")] pub(crate) kind: FollowType, pub(crate) id: Url, - - #[serde(flatten)] - pub(crate) unparsed: Unparsed, } diff --git a/crates/apub/src/protocol/activities/following/undo_follow.rs b/crates/apub/src/protocol/activities/following/undo_follow.rs index 38306b7c..201601b1 100644 --- a/crates/apub/src/protocol/activities/following/undo_follow.rs +++ b/crates/apub/src/protocol/activities/following/undo_follow.rs @@ -1,6 +1,6 @@ use crate::{ objects::person::ApubPerson, - protocol::{activities::following::follow::FollowCommunity, Unparsed}, + protocol::activities::following::follow::FollowCommunity, }; use activitypub_federation::core::object_id::ObjectId; use activitystreams_kinds::activity::UndoType; @@ -15,7 +15,4 @@ pub struct UndoFollowCommunity { #[serde(rename = "type")] pub(crate) kind: UndoType, pub(crate) id: Url, - - #[serde(flatten)] - pub(crate) unparsed: Unparsed, } diff --git a/crates/apub/src/protocol/activities/voting/undo_vote.rs b/crates/apub/src/protocol/activities/voting/undo_vote.rs index 387f3d10..ee258539 100644 --- a/crates/apub/src/protocol/activities/voting/undo_vote.rs +++ b/crates/apub/src/protocol/activities/voting/undo_vote.rs @@ -1,7 +1,4 @@ -use crate::{ - objects::person::ApubPerson, - protocol::{activities::voting::vote::Vote, Unparsed}, -}; +use crate::{objects::person::ApubPerson, protocol::activities::voting::vote::Vote}; use activitypub_federation::core::object_id::ObjectId; use activitystreams_kinds::activity::UndoType; use serde::{Deserialize, Serialize}; @@ -15,7 +12,4 @@ pub struct UndoVote { #[serde(rename = "type")] pub(crate) kind: UndoType, pub(crate) id: Url, - - #[serde(flatten)] - pub(crate) unparsed: Unparsed, } diff --git a/crates/apub/src/protocol/activities/voting/vote.rs b/crates/apub/src/protocol/activities/voting/vote.rs index 6d98862e..002d235c 100644 --- a/crates/apub/src/protocol/activities/voting/vote.rs +++ b/crates/apub/src/protocol/activities/voting/vote.rs @@ -1,8 +1,4 @@ -use crate::{ - fetcher::post_or_comment::PostOrComment, - objects::person::ApubPerson, - protocol::Unparsed, -}; +use crate::{fetcher::post_or_comment::PostOrComment, objects::person::ApubPerson}; use activitypub_federation::core::object_id::ObjectId; use lemmy_utils::error::LemmyError; use serde::{Deserialize, Serialize}; @@ -18,9 +14,6 @@ pub struct Vote { #[serde(rename = "type")] pub(crate) kind: VoteType, pub(crate) id: Url, - - #[serde(flatten)] - pub(crate) unparsed: Unparsed, } #[derive(Clone, Debug, Display, Deserialize, Serialize, PartialEq, Eq)]