]> Untitled Git - lemmy.git/blob - server/src/routes/webfinger.rs
Merge branch 'admin_settings' into dev
[lemmy.git] / server / src / routes / webfinger.rs
1 use crate::db::community::Community;
2 use crate::Settings;
3 use actix_web::web;
4 use actix_web::web::Query;
5 use actix_web::HttpResponse;
6 use diesel::r2d2::{ConnectionManager, Pool};
7 use diesel::PgConnection;
8 use regex::Regex;
9 use serde::Deserialize;
10 use serde_json::json;
11
12 #[derive(Deserialize)]
13 pub struct Params {
14   resource: String,
15 }
16
17 pub fn config(cfg: &mut web::ServiceConfig) {
18   if Settings::get().federation_enabled {
19     cfg.route(
20       ".well-known/webfinger",
21       web::get().to(get_webfinger_response),
22     );
23   }
24 }
25
26 lazy_static! {
27   static ref WEBFINGER_COMMUNITY_REGEX: Regex = Regex::new(&format!(
28     "^group:([a-z0-9_]{{3, 20}})@{}$",
29     Settings::get().hostname
30   ))
31   .unwrap();
32 }
33
34 /// Responds to webfinger requests of the following format. There isn't any real documentation for
35 /// this, but it described in this blog post:
36 /// https://mastodon.social/.well-known/webfinger?resource=acct:gargron@mastodon.social
37 ///
38 /// You can also view the webfinger response that Mastodon sends:
39 /// https://radical.town/.well-known/webfinger?resource=acct:felix@radical.town
40 async fn get_webfinger_response(
41   info: Query<Params>,
42   db: web::Data<Pool<ConnectionManager<PgConnection>>>,
43 ) -> Result<HttpResponse, actix_web::Error> {
44   let res = web::block(move || {
45     let conn = db.get()?;
46
47     let regex_parsed = WEBFINGER_COMMUNITY_REGEX
48       .captures(&info.resource)
49       .map(|c| c.get(1));
50     // TODO: replace this with .flatten() once we are running rust 1.40
51     let regex_parsed_flattened = match regex_parsed {
52       Some(s) => s,
53       None => None,
54     };
55     let community_name = match regex_parsed_flattened {
56       Some(c) => c.as_str(),
57       None => return Err(format_err!("not_found")),
58     };
59
60     // Make sure the requested community exists.
61     let community = match Community::read_from_name(&conn, community_name.to_string()) {
62       Ok(o) => o,
63       Err(_) => return Err(format_err!("not_found")),
64     };
65
66     let community_url = community.get_url();
67
68     Ok(json!({
69     "subject": info.resource,
70     "aliases": [
71       community_url,
72     ],
73     "links": [
74     {
75       "rel": "http://webfinger.net/rel/profile-page",
76       "type": "text/html",
77       "href": community_url
78     },
79     {
80       "rel": "self",
81       "type": "application/activity+json",
82       // Yes this is correct, this link doesn't include the `.json` extension
83       "href": community_url
84     }
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   .await
94   .map(|json| HttpResponse::Ok().json(json))
95   .map_err(|_| HttpResponse::InternalServerError())?;
96   Ok(res)
97 }