]> Untitled Git - lemmy.git/blob - crates/routes/src/webfinger.rs
Remove header guard for activitypub routes
[lemmy.git] / crates / routes / src / webfinger.rs
1 use actix_web::{web, web::Query, HttpResponse};
2 use anyhow::anyhow;
3 use lemmy_api_common::blocking;
4 use lemmy_apub_lib::webfinger::{WebfingerLink, WebfingerResponse};
5 use lemmy_db_schema::source::{community::Community, person::Person};
6 use lemmy_utils::{settings::structs::Settings, ApiError, LemmyError};
7 use lemmy_websocket::LemmyContext;
8 use serde::Deserialize;
9
10 #[derive(Deserialize)]
11 struct Params {
12   resource: String,
13 }
14
15 pub fn config(cfg: &mut web::ServiceConfig, settings: &Settings) {
16   if settings.federation.enabled {
17     cfg.route(
18       ".well-known/webfinger",
19       web::get().to(get_webfinger_response),
20     );
21   }
22 }
23
24 /// Responds to webfinger requests of the following format. There isn't any real documentation for
25 /// this, but it described in this blog post:
26 /// https://mastodon.social/.well-known/webfinger?resource=acct:gargron@mastodon.social
27 ///
28 /// You can also view the webfinger response that Mastodon sends:
29 /// https://radical.town/.well-known/webfinger?resource=acct:felix@radical.town
30 async fn get_webfinger_response(
31   info: Query<Params>,
32   context: web::Data<LemmyContext>,
33 ) -> Result<HttpResponse, LemmyError> {
34   let community_regex_parsed = context
35     .settings()
36     .webfinger_community_regex()
37     .captures(&info.resource)
38     .map(|c| c.get(1))
39     .flatten();
40
41   let username_regex_parsed = context
42     .settings()
43     .webfinger_username_regex()
44     .captures(&info.resource)
45     .map(|c| c.get(1))
46     .flatten();
47
48   let url = if let Some(community_name) = community_regex_parsed {
49     let community_name = community_name.as_str().to_owned();
50     // Make sure the requested community exists.
51     blocking(context.pool(), move |conn| {
52       Community::read_from_name(conn, &community_name)
53     })
54     .await?
55     .map_err(|e| ApiError::err("not_found", e))?
56     .actor_id
57   } else if let Some(person_name) = username_regex_parsed {
58     let person_name = person_name.as_str().to_owned();
59     // Make sure the requested person exists.
60     blocking(context.pool(), move |conn| {
61       Person::find_by_name(conn, &person_name)
62     })
63     .await?
64     .map_err(|e| ApiError::err("not_found", e))?
65     .actor_id
66   } else {
67     return Err(LemmyError::from(anyhow!("not_found")));
68   };
69
70   let json = WebfingerResponse {
71     subject: info.resource.to_owned(),
72     aliases: vec![url.to_owned().into()],
73     links: vec![
74       WebfingerLink {
75         rel: Some("http://webfinger.net/rel/profile-page".to_string()),
76         type_: Some("text/html".to_string()),
77         href: Some(url.to_owned().into()),
78         template: None,
79       },
80       WebfingerLink {
81         rel: Some("self".to_string()),
82         type_: Some("application/activity+json".to_string()),
83         href: Some(url.into()),
84         template: None,
85       }, // TODO: this also needs to return the subscribe link once that's implemented
86          //{
87          //  "rel": "http://ostatus.org/schema/1.0/subscribe",
88          //  "template": "https://my_instance.com/authorize_interaction?uri={uri}"
89          //}
90     ],
91   };
92
93   Ok(HttpResponse::Ok().json(json))
94 }