From e3b715002b02149c863bafcaa8133745653b88ec Mon Sep 17 00:00:00 2001 From: Nutomic Date: Fri, 5 Jan 2024 17:03:13 +0100 Subject: [PATCH] Handle federated reports from Mastodon, Kbin (#4323) * Test Kbin/Mbin federation * Handle reports from Mastodon/Kbin (fixes #4217) * prettier * revert * add mastodon activity * ci * revert * ci --- Cargo.lock | 4 +- Cargo.toml | 2 +- .../apub/assets/mastodon/activities/flag.json | 13 ++++++ .../apub/assets/mbin/activities/accept.json | 12 +++++ crates/apub/assets/mbin/activities/flag.json | 11 +++++ .../apub/src/activities/community/report.rs | 15 ++++-- .../protocol/activities/community/report.rs | 46 +++++++++++++++++-- crates/apub/src/protocol/activities/mod.rs | 12 ++++- 8 files changed, 102 insertions(+), 13 deletions(-) create mode 100644 crates/apub/assets/mastodon/activities/flag.json create mode 100644 crates/apub/assets/mbin/activities/accept.json create mode 100644 crates/apub/assets/mbin/activities/flag.json diff --git a/Cargo.lock b/Cargo.lock index ba19a1961..73aa51ef9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,9 +10,9 @@ checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" [[package]] name = "activitypub_federation" -version = "0.5.0" +version = "0.5.1-beta.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bac58c1d61b6e2358adbd043c78ba853428102b489acb7b6cb74ee6f2ae668f" +checksum = "866db431760d14a7360f12e75ad48f3265b5b89cd2303e548a02bcc8983e4fcd" dependencies = [ "activitystreams-kinds", "actix-web", diff --git a/Cargo.toml b/Cargo.toml index c54255e41..bfb275b37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -95,7 +95,7 @@ lemmy_routes = { version = "=0.19.2-rc.2", path = "./crates/routes" } lemmy_db_views = { version = "=0.19.2-rc.2", path = "./crates/db_views" } lemmy_db_views_actor = { version = "=0.19.2-rc.2", path = "./crates/db_views_actor" } lemmy_db_views_moderator = { version = "=0.19.2-rc.2", path = "./crates/db_views_moderator" } -activitypub_federation = { version = "0.5.0", default-features = false, features = [ +activitypub_federation = { version = "0.5.1-beta.1", default-features = false, features = [ "actix-web", ] } diesel = "2.1.4" diff --git a/crates/apub/assets/mastodon/activities/flag.json b/crates/apub/assets/mastodon/activities/flag.json new file mode 100644 index 000000000..83f4c0817 --- /dev/null +++ b/crates/apub/assets/mastodon/activities/flag.json @@ -0,0 +1,13 @@ +{ + "@context": "https://www.w3.org/ns/activitystreams", + "id": "https://mastodon.example/ccb4f39a-506a-490e-9a8c-71831c7713a4", + "type": "Flag", + "actor": "https://mastodon.example/actor", + "content": "Please take a look at this user and their posts", + "object": [ + "https://example.com/users/1", + "https://example.com/posts/380590", + "https://example.com/posts/380591" + ], + "to": "https://example.com/users/1" +} diff --git a/crates/apub/assets/mbin/activities/accept.json b/crates/apub/assets/mbin/activities/accept.json new file mode 100644 index 000000000..3a190977c --- /dev/null +++ b/crates/apub/assets/mbin/activities/accept.json @@ -0,0 +1,12 @@ +{ + "@context": "https://www.w3.org/ns/activitystreams", + "id": "https://some-mbin.instance/f/object/2721ffc3-f8a9-417e-a124-af057434a3af#accept", + "type": "Accept", + "actor": "https://some-mbin.instance/m/someMag", + "object": { + "id": "https://some-other.instance/f/object/c51ea652-e594-4920-a989-f5350f0cec05", + "type": "Follow", + "actor": "https://some-other.instance/u/someUser", + "object": "https://some-mbin.instance/m/someMag" + } +} diff --git a/crates/apub/assets/mbin/activities/flag.json b/crates/apub/assets/mbin/activities/flag.json new file mode 100644 index 000000000..7c1e5ae23 --- /dev/null +++ b/crates/apub/assets/mbin/activities/flag.json @@ -0,0 +1,11 @@ +{ + "@context": ["https://www.w3.org/ns/activitystreams"], + "id": "https://mbin-test1/reports/45f8a01d-a73e-4575-bffa-c9f24c61f458", + "type": "Flag", + "actor": "https://mbin-test1/u/BentiGorlich", + "object": ["https://lemmy-test/post/4", "https://lemmy-test/u/BentiGorlich"], + "audience": "https://lemmy-test/c/test_mag", + "summary": "dikjhgasdpas dsaĆ¼", + "content": "dikjhgasdpas dsaĆ¼", + "to": ["https://lemmy-test/c/test_mag"] +} diff --git a/crates/apub/src/activities/community/report.rs b/crates/apub/src/activities/community/report.rs index 974810f61..70278075c 100644 --- a/crates/apub/src/activities/community/report.rs +++ b/crates/apub/src/activities/community/report.rs @@ -2,7 +2,10 @@ use crate::{ activities::{generate_activity_id, send_lemmy_activity, verify_person_in_community}, insert_received_activity, objects::{community::ApubCommunity, instance::ApubSite, person::ApubPerson}, - protocol::{activities::community::report::Report, InCommunity}, + protocol::{ + activities::community::report::{Report, ReportObject}, + InCommunity, + }, PostOrComment, }; use activitypub_federation::{ @@ -45,8 +48,9 @@ impl Report { let report = Report { actor: actor.id().into(), to: [community.id().into()], - object: object_id.clone(), - summary: reason, + object: ReportObject::Lemmy(object_id.clone()), + summary: Some(reason), + content: None, kind, id: id.clone(), audience: Some(community.id().into()), @@ -97,6 +101,7 @@ impl ActivityHandler for Report { #[tracing::instrument(skip_all)] async fn receive(self, context: &Data) -> Result<(), LemmyError> { let actor = self.actor.dereference(context).await?; + let reason = self.reason()?; match self.object.dereference(context).await? { PostOrComment::Post(post) => { let report_form = PostReportForm { @@ -104,7 +109,7 @@ impl ActivityHandler for Report { post_id: post.id, original_post_name: post.name.clone(), original_post_url: post.url.clone(), - reason: self.summary.clone(), + reason, original_post_body: post.body.clone(), }; PostReport::report(&mut context.pool(), &report_form).await?; @@ -114,7 +119,7 @@ impl ActivityHandler for Report { creator_id: actor.id, comment_id: comment.id, original_comment_text: comment.content.clone(), - reason: self.summary.clone(), + reason, }; CommentReport::report(&mut context.pool(), &report_form).await?; } diff --git a/crates/apub/src/protocol/activities/community/report.rs b/crates/apub/src/protocol/activities/community/report.rs index 6e8a1bbf3..adcddfdfc 100644 --- a/crates/apub/src/protocol/activities/community/report.rs +++ b/crates/apub/src/protocol/activities/community/report.rs @@ -11,7 +11,7 @@ use activitypub_federation::{ protocol::helpers::deserialize_one, }; use lemmy_api_common::context::LemmyContext; -use lemmy_utils::error::LemmyError; +use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult}; use serde::{Deserialize, Serialize}; use url::Url; @@ -21,14 +21,54 @@ pub struct Report { pub(crate) actor: ObjectId, #[serde(deserialize_with = "deserialize_one")] pub(crate) to: [ObjectId; 1], - pub(crate) object: ObjectId, - pub(crate) summary: String, + pub(crate) object: ReportObject, + /// Report reason as sent by Lemmy + pub(crate) summary: Option, + /// Report reason as sent by Mastodon + pub(crate) content: Option, #[serde(rename = "type")] pub(crate) kind: FlagType, pub(crate) id: Url, pub(crate) audience: Option>, } +impl Report { + pub fn reason(&self) -> LemmyResult { + self + .summary + .clone() + .or(self.content.clone()) + .ok_or(LemmyErrorType::CouldntFindObject.into()) + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(untagged)] +pub(crate) enum ReportObject { + Lemmy(ObjectId), + /// Mastodon sends an array containing user id and one or more post ids + Mastodon(Vec), +} + +impl ReportObject { + pub async fn dereference(self, context: &Data) -> LemmyResult { + match self { + ReportObject::Lemmy(l) => l.dereference(context).await, + ReportObject::Mastodon(objects) => { + for o in objects { + // Find the first reported item which can be dereferenced as post or comment (Lemmy can + // only handle one item per report). + let deref = ObjectId::from(o).dereference(context).await; + if deref.is_ok() { + return deref; + } + } + Err(LemmyErrorType::CouldntFindObject.into()) + } + } + } +} + #[async_trait::async_trait] impl InCommunity for Report { async fn community(&self, context: &Data) -> Result { diff --git a/crates/apub/src/protocol/activities/mod.rs b/crates/apub/src/protocol/activities/mod.rs index e7c5f64d0..024f08929 100644 --- a/crates/apub/src/protocol/activities/mod.rs +++ b/crates/apub/src/protocol/activities/mod.rs @@ -18,10 +18,10 @@ pub enum CreateOrUpdateType { mod tests { use crate::protocol::{ activities::{ - community::announce::AnnounceActivity, + community::{announce::AnnounceActivity, report::Report}, create_or_update::{note::CreateOrUpdateNote, page::CreateOrUpdatePage}, deletion::delete::Delete, - following::{follow::Follow, undo_follow::UndoFollow}, + following::{accept::AcceptFollow, follow::Follow, undo_follow::UndoFollow}, voting::{undo_vote::UndoVote, vote::Vote}, }, tests::test_json, @@ -50,6 +50,7 @@ mod tests { test_json::("assets/mastodon/activities/undo_follow.json")?; test_json::("assets/mastodon/activities/like_page.json")?; test_json::("assets/mastodon/activities/undo_like_page.json")?; + test_json::("assets/mastodon/activities/flag.json")?; Ok(()) } @@ -88,4 +89,11 @@ mod tests { test_json::("assets/peertube/activities/announce_video.json")?; Ok(()) } + + #[test] + fn test_parse_mbin_activities() -> LemmyResult<()> { + test_json::("assets/mbin/activities/accept.json")?; + test_json::("assets/mbin/activities/flag.json")?; + Ok(()) + } }