From 5859502a2a788bf8160ecdaeb79ed24a06f98729 Mon Sep 17 00:00:00 2001 From: Nutomic Date: Fri, 8 Mar 2024 16:23:15 +0100 Subject: [PATCH] Fix missing private key for signed fetch (#4516) * Fix missing private key for signed fetch (fixes #4451) * clippy * instance actor name and webfinger * better webfinger handling * upgrade lib * update test asset --- Cargo.lock | 6 +- Cargo.toml | 2 +- .../apub/assets/lemmy/objects/instance.json | 1 + crates/apub/src/objects/instance.rs | 1 + crates/apub/src/protocol/objects/instance.rs | 4 +- crates/db_schema/src/source/site.rs | 2 + crates/db_views/src/site_view.rs | 7 +- crates/routes/src/webfinger.rs | 75 +++++++++++-------- 8 files changed, 55 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e4c55f082..b9ebfa540 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,9 +16,9 @@ checksum = "8f27d075294830fcab6f66e320dab524bc6d048f4a151698e153205559113772" [[package]] name = "activitypub_federation" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee115a53849bbcac6c953495b5f9322b56680c0a1bdef6813183f0453f9ee3c" +checksum = "a028034c642d3ed16b535f98f48b3df30397833c183d68852d79de16650d5ed5" dependencies = [ "activitystreams-kinds", "actix-web", @@ -2577,7 +2577,6 @@ version = "0.19.3" dependencies = [ "activitypub_federation", "actix-web", - "anyhow", "chrono", "encoding", "enum-map", @@ -2599,7 +2598,6 @@ dependencies = [ "serde", "serde_with", "serial_test", - "task-local-extensions", "tokio", "tracing", "ts-rs", diff --git a/Cargo.toml b/Cargo.toml index 037d03919..5e0cdb16e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -96,7 +96,7 @@ lemmy_routes = { version = "=0.19.3", path = "./crates/routes" } lemmy_db_views = { version = "=0.19.3", path = "./crates/db_views" } lemmy_db_views_actor = { version = "=0.19.3", path = "./crates/db_views_actor" } lemmy_db_views_moderator = { version = "=0.19.3", path = "./crates/db_views_moderator" } -activitypub_federation = { version = "0.5.1", default-features = false, features = [ +activitypub_federation = { version = "0.5.2", default-features = false, features = [ "actix-web", ] } diesel = "2.1.4" diff --git a/crates/apub/assets/lemmy/objects/instance.json b/crates/apub/assets/lemmy/objects/instance.json index 1e07043d9..92053bac6 100644 --- a/crates/apub/assets/lemmy/objects/instance.json +++ b/crates/apub/assets/lemmy/objects/instance.json @@ -2,6 +2,7 @@ "type": "Application", "id": "https://enterprise.lemmy.ml/", "name": "Enterprise", + "preferredUsername": "enterprise.lemmy.ml", "summary": "A test instance", "content": "

Enterprise sidebar

\\n", "mediaType": "text/html", diff --git a/crates/apub/src/objects/instance.rs b/crates/apub/src/objects/instance.rs index 165378ab6..8f4f163db 100644 --- a/crates/apub/src/objects/instance.rs +++ b/crates/apub/src/objects/instance.rs @@ -96,6 +96,7 @@ impl Object for ApubSite { kind: ApplicationType::Application, id: self.id().into(), name: self.name.clone(), + preferred_username: data.domain().to_string(), content: self.sidebar.as_ref().map(|d| markdown_to_html(d)), source: self.sidebar.clone().map(Source::new), summary: self.description.clone(), diff --git a/crates/apub/src/protocol/objects/instance.rs b/crates/apub/src/protocol/objects/instance.rs index 17623719c..8f6a0f368 100644 --- a/crates/apub/src/protocol/objects/instance.rs +++ b/crates/apub/src/protocol/objects/instance.rs @@ -19,8 +19,10 @@ pub struct Instance { #[serde(rename = "type")] pub(crate) kind: ApplicationType, pub(crate) id: ObjectId, - // site name + /// site name pub(crate) name: String, + /// instance domain, necessary for mastodon authorized fetch + pub(crate) preferred_username: String, pub(crate) inbox: Url, /// mandatory field in activitypub, lemmy currently serves an empty outbox pub(crate) outbox: Url, diff --git a/crates/db_schema/src/source/site.rs b/crates/db_schema/src/source/site.rs index 754acb2aa..40ba14f96 100644 --- a/crates/db_schema/src/source/site.rs +++ b/crates/db_schema/src/source/site.rs @@ -34,7 +34,9 @@ pub struct Site { pub last_refreshed_at: DateTime, /// The site inbox pub inbox_url: DbUrl, + #[serde(skip)] pub private_key: Option, + #[serde(skip)] pub public_key: String, pub instance_id: InstanceId, /// If present, nsfw content is visible by default. Should be displayed by frontends/clients diff --git a/crates/db_views/src/site_view.rs b/crates/db_views/src/site_view.rs index 17819fdd7..52074c982 100644 --- a/crates/db_views/src/site_view.rs +++ b/crates/db_views/src/site_view.rs @@ -9,7 +9,7 @@ use lemmy_db_schema::{ impl SiteView { pub async fn read_local(pool: &mut DbPool<'_>) -> Result { let conn = &mut get_conn(pool).await?; - let mut res = site::table + site::table .inner_join(local_site::table) .inner_join( local_site_rate_limit::table.on(local_site::id.eq(local_site_rate_limit::local_site_id)), @@ -22,9 +22,6 @@ impl SiteView { site_aggregates::all_columns, )) .first::(conn) - .await?; - - res.site.private_key = None; - Ok(res) + .await } } diff --git a/crates/routes/src/webfinger.rs b/crates/routes/src/webfinger.rs index b9da07c56..95092b244 100644 --- a/crates/routes/src/webfinger.rs +++ b/crates/routes/src/webfinger.rs @@ -38,43 +38,54 @@ async fn get_webfinger_response( ) -> Result { let name = extract_webfinger_name(&info.resource, &context)?; - let user_id: Option = Person::read_from_name(&mut context.pool(), name, false) - .await - .ok() - .map(|c| c.actor_id.into()); - let community_id: Option = Community::read_from_name(&mut context.pool(), name, false) - .await - .ok() - .and_then(|c| { - if c.visibility == CommunityVisibility::Public { - let id: Url = c.actor_id.into(); - Some(id) - } else { - None - } - }); + let links = if name == context.settings().hostname { + // webfinger response for instance actor (required for mastodon authorized fetch) + let url = Url::parse(&context.settings().get_protocol_and_hostname())?; + vec![webfinger_link_for_actor(Some(url), "none", &context)] + } else { + // webfinger response for user/community + let user_id: Option = Person::read_from_name(&mut context.pool(), name, false) + .await + .ok() + .map(|c| c.actor_id.into()); + let community_id: Option = Community::read_from_name(&mut context.pool(), name, false) + .await + .ok() + .and_then(|c| { + if c.visibility == CommunityVisibility::Public { + let id: Url = c.actor_id.into(); + Some(id) + } else { + None + } + }); - // Mastodon seems to prioritize the last webfinger item in case of duplicates. Put - // community last so that it gets prioritized. For Lemmy the order doesnt matter. - let links = vec![ - webfinger_link_for_actor(user_id, "Person", &context), - webfinger_link_for_actor(community_id, "Group", &context), - ] + // Mastodon seems to prioritize the last webfinger item in case of duplicates. Put + // community last so that it gets prioritized. For Lemmy the order doesnt matter. + vec![ + webfinger_link_for_actor(user_id, "Person", &context), + webfinger_link_for_actor(community_id, "Group", &context), + ] + } .into_iter() .flatten() - .collect(); + .collect::>(); - let json = Webfinger { - subject: info.resource.clone(), - links, - ..Default::default() - }; + if links.is_empty() { + Ok(HttpResponse::NotFound().finish()) + } else { + let json = Webfinger { + subject: info.resource.clone(), + links, + ..Default::default() + }; - Ok( - HttpResponse::Ok() - .content_type(&WEBFINGER_CONTENT_TYPE) - .json(json), - ) + Ok( + HttpResponse::Ok() + .content_type(&WEBFINGER_CONTENT_TYPE) + .json(json), + ) + } } fn webfinger_link_for_actor(