diff options
author | Felix Ableitner <me@nutomic.com> | 2019-12-18 01:59:47 +0100 |
---|---|---|
committer | Felix Ableitner <me@nutomic.com> | 2019-12-27 17:29:50 +0100 |
commit | 3d9f7d28d69bf077258c1c8e0803be79a12e426a (patch) | |
tree | 170b560bf97754caa004e26a2984a55fd6fe5e65 | |
parent | 1f7546789a3e9d4a095aa72ba69843259eb56fc6 (diff) |
Implement webfinger (fixes #149)
-rw-r--r-- | server/src/db/community.rs | 4 | ||||
-rw-r--r-- | server/src/db/user.rs | 20 | ||||
-rw-r--r-- | server/src/feeds.rs | 6 | ||||
-rw-r--r-- | server/src/lib.rs | 1 | ||||
-rw-r--r-- | server/src/main.rs | 6 | ||||
-rw-r--r-- | server/src/settings.rs | 1 | ||||
-rw-r--r-- | server/src/webfinger.rs | 69 |
7 files changed, 94 insertions, 13 deletions
diff --git a/server/src/db/community.rs b/server/src/db/community.rs index 57b962d1..74579535 100644 --- a/server/src/db/community.rs +++ b/server/src/db/community.rs @@ -68,6 +68,10 @@ impl Community { .filter(name.eq(community_name)) .first::<Self>(conn) } + + pub fn get_community_url(community_name: &str) -> String { + format!("https://{}/c/{}", Settings::get().hostname, community_name) + } } #[derive(Identifiable, Queryable, Associations, PartialEq, Debug)] diff --git a/server/src/db/user.rs b/server/src/db/user.rs index 3d3865e8..21407e58 100644 --- a/server/src/db/user.rs +++ b/server/src/db/user.rs @@ -132,23 +132,27 @@ impl User_ { .unwrap() } + pub fn find_by_username(conn: &PgConnection, username: &str) -> Result<Self, Error> { + user_.filter(name.eq(username)).first::<User_>(conn) + } + + pub fn find_by_email(conn: &PgConnection, from_email: &str) -> Result<Self, Error> { + user_.filter(email.eq(from_email)).first::<User_>(conn) + } + pub fn find_by_email_or_username( conn: &PgConnection, username_or_email: &str, ) -> Result<Self, Error> { if is_email_regex(username_or_email) { - user_ - .filter(email.eq(username_or_email)) - .first::<User_>(conn) + User_::find_by_email(conn, username_or_email) } else { - user_ - .filter(name.eq(username_or_email)) - .first::<User_>(conn) + User_::find_by_username(conn, username_or_email) } } - pub fn find_by_email(conn: &PgConnection, from_email: &str) -> Result<Self, Error> { - user_.filter(email.eq(from_email)).first::<User_>(conn) + pub fn get_user_profile_url(username: &str) -> String { + format!("https://{}/u/{}", Settings::get().hostname, username) } pub fn find_by_jwt(conn: &PgConnection, jwt: &str) -> Result<Self, Error> { diff --git a/server/src/feeds.rs b/server/src/feeds.rs index 5337edcf..c735688b 100644 --- a/server/src/feeds.rs +++ b/server/src/feeds.rs @@ -110,8 +110,8 @@ fn get_feed_user(sort_type: &SortType, user_name: String) -> Result<String, Erro let conn = establish_connection(); let site_view = SiteView::read(&conn)?; - let user = User_::find_by_email_or_username(&conn, &user_name)?; - let user_url = format!("https://{}/u/{}", Settings::get().hostname, user.name); + let user = User_::find_by_username(&conn, &user_name)?; + let user_url = User_::get_user_profile_url(&user_name); let posts = PostQueryBuilder::create(&conn) .listing_type(ListingType::All) @@ -135,7 +135,7 @@ fn get_feed_community(sort_type: &SortType, community_name: String) -> Result<St let site_view = SiteView::read(&conn)?; let community = Community::read_from_name(&conn, community_name)?; - let community_url = format!("https://{}/c/{}", Settings::get().hostname, community.name); + let community_url = Community::get_community_url(&community.name); let posts = PostQueryBuilder::create(&conn) .listing_type(ListingType::All) diff --git a/server/src/lib.rs b/server/src/lib.rs index c23c97d2..7afe4947 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -30,6 +30,7 @@ pub mod nodeinfo; pub mod schema; pub mod settings; pub mod version; +pub mod webfinger; pub mod websocket; use crate::settings::Settings; diff --git a/server/src/main.rs b/server/src/main.rs index 2d657a61..29f361ea 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -11,6 +11,7 @@ use lemmy_server::db::establish_connection; use lemmy_server::feeds; use lemmy_server::nodeinfo; use lemmy_server::settings::Settings; +use lemmy_server::webfinger; use lemmy_server::websocket::server::*; use std::env; use std::time::{Duration, Instant}; @@ -255,7 +256,10 @@ fn main() { ) .route( "/federation/u/{user_name}", - web::get().to(apub::user::get_apub_user), + web::get().to(apub::user::get_apub_user)) + .route( + ".well-known/webfinger", + web::get().to(webfinger::get_webfinger_response), ) }) .bind((settings.bind, settings.port)) diff --git a/server/src/settings.rs b/server/src/settings.rs index a7203a1e..4df2979e 100644 --- a/server/src/settings.rs +++ b/server/src/settings.rs @@ -56,7 +56,6 @@ lazy_static! { } impl Settings { - /// Reads config from the files and environment. /// First, defaults are loaded from CONFIG_FILE_DEFAULTS, then these values can be overwritten /// from CONFIG_FILE (optional). Finally, values from the environment (with prefix LEMMY) are diff --git a/server/src/webfinger.rs b/server/src/webfinger.rs new file mode 100644 index 00000000..47e2037b --- /dev/null +++ b/server/src/webfinger.rs @@ -0,0 +1,69 @@ +use crate::db::community::Community; +use crate::db::establish_connection; +use crate::Settings; +use actix_web::body::Body; +use actix_web::web::Query; +use actix_web::HttpResponse; +use serde::Deserialize; +use serde_json::json; + +#[derive(Deserialize)] +pub struct Params { + resource: String, +} + +/// Responds to webfinger requests of the following format. There isn't any real documentation for +/// this, but it described in this blog post: +/// https://mastodon.social/.well-known/webfinger?resource=acct:gargron@mastodon.social +/// +/// You can also view the webfinger response that Mastodon sends: +/// https://radical.town/.well-known/webfinger?resource=acct:felix@radical.town +pub fn get_webfinger_response(info: Query<Params>) -> HttpResponse<Body> { + // NOTE: Calling the parameter "account" maybe doesn't really make sense, but should give us the + // best compatibility with existing implementations. We could also support an alternative name + // like "group", and encourage others to use that. + let community_identifier = info.resource.replace("acct:", ""); + let split_identifier: Vec<&str> = community_identifier.split("@").collect(); + let community_name = split_identifier[0]; + // It looks like Mastodon does not return webfinger requests for users from other instances, so we + // don't do that either. + if split_identifier.len() != 2 || split_identifier[1] != Settings::get().hostname { + return HttpResponse::NotFound().finish(); + } + + // Make sure the requested community exists. + let conn = establish_connection(); + match Community::read_from_name(&conn, community_name.to_owned()) { + Err(_) => return HttpResponse::NotFound().finish(), + Ok(c) => c, + }; + + let community_url = Community::get_community_url(&community_name); + + let json = json!({ + "subject": info.resource, + "aliases": [ + community_url, + ], + "links": [ + { + "rel": "http://webfinger.net/rel/profile-page", + "type": "text/html", + "href": community_url + }, + { + "rel": "self", + "type": "application/activity+json", + "href": community_url // Yes this is correct, this link doesn't include the `.json` extension + } + // TODO: this also needs to return the subscribe link once that's implemented + //{ + // "rel": "http://ostatus.org/schema/1.0/subscribe", + // "template": "https://my_instance.com/authorize_interaction?uri={uri}" + //} + ] + }); + return HttpResponse::Ok() + .content_type("application/activity+json") + .body(json.to_string()); +} |