From 7c146272c3585e7fb1129c04da8591ed4de2e022 Mon Sep 17 00:00:00 2001 From: Nutomic Date: Tue, 7 May 2024 22:20:43 +0200 Subject: [PATCH] Federate with wordpress, improvements for NodeBB, Discourse federation (#4692) * Federate with wordpress * upgrade apub lib with fix * Also read post's community from `audience` * cleanup * cargo update * upgrade apub lib * add wordpress test activity --- Cargo.lock | 4 +- Cargo.toml | 2 +- .../assets/wordpress/activities/announce.json | 49 ++++++++++++ .../apub/assets/wordpress/objects/group.json | 66 +++++++++++++++++ .../apub/assets/wordpress/objects/note.json | 24 ++++++ .../apub/assets/wordpress/objects/page.json | 26 +++++++ .../apub/assets/wordpress/objects/person.json | 74 +++++++++++++++++++ crates/apub/src/protocol/activities/mod.rs | 6 ++ crates/apub/src/protocol/objects/mod.rs | 9 +++ crates/apub/src/protocol/objects/page.rs | 9 ++- crates/utils/src/error.rs | 2 - 11 files changed, 264 insertions(+), 7 deletions(-) create mode 100644 crates/apub/assets/wordpress/activities/announce.json create mode 100644 crates/apub/assets/wordpress/objects/group.json create mode 100644 crates/apub/assets/wordpress/objects/note.json create mode 100644 crates/apub/assets/wordpress/objects/page.json create mode 100644 crates/apub/assets/wordpress/objects/person.json diff --git a/Cargo.lock b/Cargo.lock index 78ab0e924..36cb9d537 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,9 +16,9 @@ checksum = "8f27d075294830fcab6f66e320dab524bc6d048f4a151698e153205559113772" [[package]] name = "activitypub_federation" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e54fe65c4a4b57712d8e436f1fb86ff37e10b56f011f4233fbbfa8c669163e9d" +checksum = "ac8ff2d0151ce9ac02eb29e4a58b41d28693f141f7963d4bfabd2f9d402ecec7" dependencies = [ "activitystreams-kinds", "actix-web", diff --git a/Cargo.toml b/Cargo.toml index aec09093d..079f4abbf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -99,7 +99,7 @@ lemmy_db_views = { version = "=0.19.4-beta.6", path = "./crates/db_views" } lemmy_db_views_actor = { version = "=0.19.4-beta.6", path = "./crates/db_views_actor" } lemmy_db_views_moderator = { version = "=0.19.4-beta.6", path = "./crates/db_views_moderator" } lemmy_federate = { version = "=0.19.4-beta.6", path = "./crates/federate" } -activitypub_federation = { version = "0.5.5", default-features = false, features = [ +activitypub_federation = { version = "0.5.6", default-features = false, features = [ "actix-web", ] } diesel = "2.1.6" diff --git a/crates/apub/assets/wordpress/activities/announce.json b/crates/apub/assets/wordpress/activities/announce.json new file mode 100644 index 000000000..985dec307 --- /dev/null +++ b/crates/apub/assets/wordpress/activities/announce.json @@ -0,0 +1,49 @@ +{ + "@context": ["https://www.w3.org/ns/activitystreams"], + "id": "https://pfefferle.org/lemmy-part-4/#activity#activity", + "type": "Announce", + "audience": "https://pfefferle.org/@pfefferle.org", + "published": "2024-05-03T12:32:29Z", + "updated": "2024-05-06T08:20:33Z", + "to": [ + "https://www.w3.org/ns/activitystreams#Public", + "https://pfefferle.org/wp-json/activitypub/1.0/actors/1/followers" + ], + "cc": [], + "object": { + "id": "https://pfefferle.org/lemmy-part-4/#activity", + "type": "Update", + "audience": "https://pfefferle.org/@pfefferle.org", + "published": "2024-05-03T12:32:29Z", + "updated": "2024-05-06T08:20:33Z", + "to": [ + "https://www.w3.org/ns/activitystreams#Public", + "https://pfefferle.org/wp-json/activitypub/1.0/actors/1/followers" + ], + "cc": [], + "object": { + "id": "https://pfefferle.org/lemmy-part-4/", + "type": "Article", + "attachment": [], + "attributedTo": "https://pfefferle.org/author/pfefferle/", + "audience": "https://pfefferle.org/@pfefferle.org", + "content": "\u003Cp\u003EIdentifies one or more entities that represent the total population of entities for which the object can considered to be relevant. Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant. \u003C/p\u003E", + "contentMap": { + "en": "\u003Cp\u003EIdentifies one or more entities that represent the total population of entities for which the object can considered to be relevant. Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant. \u003C/p\u003E" + }, + "name": "Lemmy (Part 4)", + "published": "2024-05-03T12:32:29Z", + "summary": "Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant. Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object [...]", + "tag": [], + "updated": "2024-05-06T08:20:33Z", + "url": "https://pfefferle.org/lemmy-part-4/", + "to": [ + "https://www.w3.org/ns/activitystreams#Public", + "https://pfefferle.org/wp-json/activitypub/1.0/actors/1/followers" + ], + "cc": [] + }, + "actor": "https://pfefferle.org/author/pfefferle/" + }, + "actor": "https://pfefferle.org/@pfefferle.org" +} diff --git a/crates/apub/assets/wordpress/objects/group.json b/crates/apub/assets/wordpress/objects/group.json new file mode 100644 index 000000000..35f2af0c4 --- /dev/null +++ b/crates/apub/assets/wordpress/objects/group.json @@ -0,0 +1,66 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + "https://purl.archive.org/socialweb/webfinger", + { + "schema": "http://schema.org#", + "toot": "http://joinmastodon.org/ns#", + "webfinger": "https://webfinger.net/#", + "lemmy": "https://join-lemmy.org/ns#", + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "PropertyValue": "schema:PropertyValue", + "value": "schema:value", + "Hashtag": "as:Hashtag", + "featured": { + "@id": "toot:featured", + "@type": "@id" + }, + "featuredTags": { + "@id": "toot:featuredTags", + "@type": "@id" + }, + "moderators": { + "@id": "lemmy:moderators", + "@type": "@id" + }, + "postingRestrictedToMods": "lemmy:postingRestrictedToMods", + "discoverable": "toot:discoverable", + "indexable": "toot:indexable", + "resource": "webfinger:resource" + } + ], + "id": "https://pfefferle.org/@pfefferle.org", + "type": "Group", + "attachment": [], + "attributedTo": "https://pfefferle.org/wp-json/activitypub/1.0/collections/moderators", + "name": "Matthias Pfefferle", + "icon": { + "type": "Image", + "url": "https://pfefferle.org/wp-content/uploads/2023/06/cropped-BeLItBV-_400x400.jpg" + }, + "published": "2024-04-03T16:58:22Z", + "summary": "

Webworker, blogger und podcaster

\n", + "tag": [], + "url": "https://pfefferle.org/@pfefferle.org", + "inbox": "https://pfefferle.org/wp-json/activitypub/1.0/users/0/inbox", + "outbox": "https://pfefferle.org/wp-json/activitypub/1.0/users/0/outbox", + "following": "https://pfefferle.org/wp-json/activitypub/1.0/users/0/following", + "followers": "https://pfefferle.org/wp-json/activitypub/1.0/users/0/followers", + "preferredUsername": "pfefferle.org", + "endpoints": { + "sharedInbox": "https://pfefferle.org/wp-json/activitypub/1.0/inbox" + }, + "publicKey": { + "id": "https://pfefferle.org/@pfefferle.org#main-key", + "owner": "https://pfefferle.org/@pfefferle.org", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuq8xeLMFcaCwPFBhgMRE\n/dDh2XKoNXFXnixctmK8BXSuuLMxucm3I/8NyhIvb3LqU+uP1fO8F0ecUbk2sN+x\nKag5vIV6yKXzJ8ILMWQ9AaELpXDmMZqL0zal0LUJRAOkDgPDovDAoq6tx++yDoV0\njdVbf9CoZKit1cz2ZrEuE5dswq3J/z9+c6POkhCkWEX5TPJzkOrmnjkvrXxGHUJ2\nA3+P+VaZhd5cmvqYosSpYNJshxCdev12pIF78OnYLiYiyXlgGHU+7uQR0M4tTcij\n6cUdLkms9m+b6H3ctXntPn410e5YLFPldjAYzQB5wHVdFZsWtyrbqfYdCa+KkKpA\nvwIDAQAB\n-----END PUBLIC KEY-----\n" + }, + "manuallyApprovesFollowers": false, + "featured": "https://pfefferle.org/wp-json/activitypub/1.0/users/0/collections/featured", + "moderators": "https://pfefferle.org/wp-json/activitypub/1.0/collections/moderators", + "discoverable": true, + "indexable": true, + "webfinger": "pfefferle.org@pfefferle.org", + "postingRestrictedToMods": true +} diff --git a/crates/apub/assets/wordpress/objects/note.json b/crates/apub/assets/wordpress/objects/note.json new file mode 100644 index 000000000..5b4b24da0 --- /dev/null +++ b/crates/apub/assets/wordpress/objects/note.json @@ -0,0 +1,24 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + { + "Hashtag": "as:Hashtag" + } + ], + "id": "https://pfefferle.org?c=148", + "type": "Note", + "attributedTo": "https://pfefferle.org/author/pfefferle/", + "content": "

Nice! Hello from WordPress!

", + "contentMap": { + "en": "

Nice! Hello from WordPress!

" + }, + "inReplyTo": "https://socialhub.activitypub.rocks/ap/object/ce040f1ead95964f6dbbf1084b81432d", + "published": "2024-04-30T15:21:13Z", + "tag": [], + "url": "https://pfefferle.org?c=148", + "to": [ + "https://www.w3.org/ns/activitystreams#Public", + "https://pfefferle.org/wp-json/activitypub/1.0/users/0/followers" + ], + "cc": [] +} diff --git a/crates/apub/assets/wordpress/objects/page.json b/crates/apub/assets/wordpress/objects/page.json new file mode 100644 index 000000000..1e8f4be3c --- /dev/null +++ b/crates/apub/assets/wordpress/objects/page.json @@ -0,0 +1,26 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + { + "Hashtag": "as:Hashtag" + } + ], + "id": "https://pfefferle.org/this-is-a-test-federation/", + "type": "Article", + "attachment": [], + "attributedTo": "https://pfefferle.org/author/pfefferle/", + "content": "

with Discource!

", + "contentMap": { + "en": "

with Discource!

" + }, + "name": "This is a test-federation", + "published": "2024-04-30T15:16:41Z", + "summary": "with Discource! [...]", + "tag": [], + "url": "https://pfefferle.org/this-is-a-test-federation/", + "to": [ + "https://www.w3.org/ns/activitystreams#Public", + "https://pfefferle.org/wp-json/activitypub/1.0/users/1/followers" + ], + "cc": [] +} diff --git a/crates/apub/assets/wordpress/objects/person.json b/crates/apub/assets/wordpress/objects/person.json new file mode 100644 index 000000000..6c74b53bf --- /dev/null +++ b/crates/apub/assets/wordpress/objects/person.json @@ -0,0 +1,74 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + "https://purl.archive.org/socialweb/webfinger", + { + "schema": "http://schema.org#", + "toot": "http://joinmastodon.org/ns#", + "webfinger": "https://webfinger.net/#", + "lemmy": "https://join-lemmy.org/ns#", + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "PropertyValue": "schema:PropertyValue", + "value": "schema:value", + "Hashtag": "as:Hashtag", + "featured": { + "@id": "toot:featured", + "@type": "@id" + }, + "featuredTags": { + "@id": "toot:featuredTags", + "@type": "@id" + }, + "moderators": { + "@id": "lemmy:moderators", + "@type": "@id" + }, + "postingRestrictedToMods": "lemmy:postingRestrictedToMods", + "discoverable": "toot:discoverable", + "indexable": "toot:indexable", + "resource": "webfinger:resource" + } + ], + "id": "https://pfefferle.org/author/pfefferle/", + "type": "Person", + "attachment": [ + { + "type": "PropertyValue", + "name": "Blog", + "value": "pfefferle.org" + }, + { + "type": "PropertyValue", + "name": "Profile", + "value": "pfefferle.org" + } + ], + "name": "Matthias Pfefferle", + "icon": { + "type": "Image", + "url": "https://secure.gravatar.com/avatar/a2bdca7870e859658cece96c044b3be5?s=120&d=mm&r=g" + }, + "published": "2014-02-10T15:23:08Z", + "summary": "

Ich arbeite als Open Web Lead für Automattic.

\n", + "tag": [], + "url": "https://pfefferle.org/author/pfefferle/", + "inbox": "https://pfefferle.org/wp-json/activitypub/1.0/users/1/inbox", + "outbox": "https://pfefferle.org/wp-json/activitypub/1.0/users/1/outbox", + "following": "https://pfefferle.org/wp-json/activitypub/1.0/users/1/following", + "followers": "https://pfefferle.org/wp-json/activitypub/1.0/users/1/followers", + "preferredUsername": "matthias", + "endpoints": { + "sharedInbox": "https://pfefferle.org/wp-json/activitypub/1.0/inbox" + }, + "publicKey": { + "id": "https://pfefferle.org/author/pfefferle/#main-key", + "owner": "https://pfefferle.org/author/pfefferle/", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvTA5RA40nOsso04RSwyX\nHXTojRPUMlIlArDcSy3M5GUJp9/xbxSUOdBjqd31KKB1GIi3vrLmD1Qi/ZqS95Qy\nw2Zd3xOsCg+o9bsyOG+O6Y8Lu+HEB5JKLUbNHdiSviakJ8wGadH9Wm4WIiN20y+q\n/u6lgxgiWfZ2CFCN6SOc28fUKi9NmKvXK+M12BhFfy1tC5KWXKDm0UbfI1+dmqhR\n3Ffe6vEsCI/YIVVdWxQ9kouOd0XSHOGdslktkepRO7IP9i9TdwyeCa0WWRoeO5Wa\ntVpc1Y0WuNbTM2ksIXTg0G+rO1/6KO/hrHnGu3RCfb/ZIHK5L/aWYb9B3PG3LyKV\n+wIDAQAB\n-----END PUBLIC KEY-----\n" + }, + "manuallyApprovesFollowers": false, + "featured": "https://pfefferle.org/wp-json/activitypub/1.0/users/1/collections/featured", + "discoverable": true, + "indexable": true, + "webfinger": "matthias@pfefferle.org" +} diff --git a/crates/apub/src/protocol/activities/mod.rs b/crates/apub/src/protocol/activities/mod.rs index 024f08929..ce0808ee9 100644 --- a/crates/apub/src/protocol/activities/mod.rs +++ b/crates/apub/src/protocol/activities/mod.rs @@ -96,4 +96,10 @@ mod tests { test_json::("assets/mbin/activities/flag.json")?; Ok(()) } + + #[test] + fn test_parse_wordpress_activities() -> LemmyResult<()> { + test_json::("assets/wordpress/activities/announce.json")?; + Ok(()) + } } diff --git a/crates/apub/src/protocol/objects/mod.rs b/crates/apub/src/protocol/objects/mod.rs index d8b471a6d..a9eb74e0c 100644 --- a/crates/apub/src/protocol/objects/mod.rs +++ b/crates/apub/src/protocol/objects/mod.rs @@ -206,4 +206,13 @@ mod tests { test_json::("assets/nodebb/objects/person.json")?; Ok(()) } + + #[test] + fn test_parse_object_wordpress() -> LemmyResult<()> { + test_json::("assets/wordpress/objects/group.json")?; + test_json::("assets/wordpress/objects/page.json")?; + test_json::("assets/wordpress/objects/person.json")?; + test_json::("assets/wordpress/objects/note.json")?; + Ok(()) + } } diff --git a/crates/apub/src/protocol/objects/page.rs b/crates/apub/src/protocol/objects/page.rs index 4d70744cf..bbb46bfaf 100644 --- a/crates/apub/src/protocol/objects/page.rs +++ b/crates/apub/src/protocol/objects/page.rs @@ -233,6 +233,10 @@ impl ActivityHandler for Page { #[async_trait::async_trait] impl InCommunity for Page { async fn community(&self, context: &Data) -> LemmyResult { + if let Some(audience) = &self.audience { + return audience.dereference(context).await; + } + let community = match &self.attributed_to { AttributedTo::Lemmy(_) => { let mut iter = self.to.iter().merge(self.cc.iter()); @@ -243,7 +247,7 @@ impl InCommunity for Page { break c; } } else { - Err(LemmyErrorType::NoCommunityFoundInCc)? + Err(LemmyErrorType::CouldntFindCommunity)?; } } } @@ -251,11 +255,12 @@ impl InCommunity for Page { p.iter() .find(|a| a.kind == PersonOrGroupType::Group) .map(|a| ObjectId::::from(a.id.clone().into_inner())) - .ok_or(LemmyErrorType::PageDoesNotSpecifyGroup)? + .ok_or(LemmyErrorType::CouldntFindCommunity)? .dereference(context) .await? } }; + if let Some(audience) = &self.audience { verify_community_matches(audience, community.actor_id.clone())?; } diff --git a/crates/utils/src/error.rs b/crates/utils/src/error.rs index 6cfd0aaed..00ef82033 100644 --- a/crates/utils/src/error.rs +++ b/crates/utils/src/error.rs @@ -99,8 +99,6 @@ pub enum LemmyErrorType { PersonIsBannedFromSite(String), InvalidVoteValue, PageDoesNotSpecifyCreator, - PageDoesNotSpecifyGroup, - NoCommunityFoundInCc, NoEmailSetup, LocalSiteNotSetup, EmailSmtpServerNeedsAPort,