Implement webfinger (fixes #149)
This commit is contained in:
parent
1f7546789a
commit
3d9f7d28d6
7 changed files with 94 additions and 13 deletions
|
@ -68,6 +68,10 @@ impl Community {
|
||||||
.filter(name.eq(community_name))
|
.filter(name.eq(community_name))
|
||||||
.first::<Self>(conn)
|
.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)]
|
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
|
||||||
|
|
|
@ -132,23 +132,27 @@ impl User_ {
|
||||||
.unwrap()
|
.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(
|
pub fn find_by_email_or_username(
|
||||||
conn: &PgConnection,
|
conn: &PgConnection,
|
||||||
username_or_email: &str,
|
username_or_email: &str,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
if is_email_regex(username_or_email) {
|
if is_email_regex(username_or_email) {
|
||||||
user_
|
User_::find_by_email(conn, username_or_email)
|
||||||
.filter(email.eq(username_or_email))
|
|
||||||
.first::<User_>(conn)
|
|
||||||
} else {
|
} else {
|
||||||
user_
|
User_::find_by_username(conn, username_or_email)
|
||||||
.filter(name.eq(username_or_email))
|
|
||||||
.first::<User_>(conn)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_email(conn: &PgConnection, from_email: &str) -> Result<Self, Error> {
|
pub fn get_user_profile_url(username: &str) -> String {
|
||||||
user_.filter(email.eq(from_email)).first::<User_>(conn)
|
format!("https://{}/u/{}", Settings::get().hostname, username)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_jwt(conn: &PgConnection, jwt: &str) -> Result<Self, Error> {
|
pub fn find_by_jwt(conn: &PgConnection, jwt: &str) -> Result<Self, Error> {
|
||||||
|
|
|
@ -110,8 +110,8 @@ fn get_feed_user(sort_type: &SortType, user_name: String) -> Result<String, Erro
|
||||||
let conn = establish_connection();
|
let conn = establish_connection();
|
||||||
|
|
||||||
let site_view = SiteView::read(&conn)?;
|
let site_view = SiteView::read(&conn)?;
|
||||||
let user = User_::find_by_email_or_username(&conn, &user_name)?;
|
let user = User_::find_by_username(&conn, &user_name)?;
|
||||||
let user_url = format!("https://{}/u/{}", Settings::get().hostname, user.name);
|
let user_url = User_::get_user_profile_url(&user_name);
|
||||||
|
|
||||||
let posts = PostQueryBuilder::create(&conn)
|
let posts = PostQueryBuilder::create(&conn)
|
||||||
.listing_type(ListingType::All)
|
.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 site_view = SiteView::read(&conn)?;
|
||||||
let community = Community::read_from_name(&conn, community_name)?;
|
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)
|
let posts = PostQueryBuilder::create(&conn)
|
||||||
.listing_type(ListingType::All)
|
.listing_type(ListingType::All)
|
||||||
|
|
|
@ -30,6 +30,7 @@ pub mod nodeinfo;
|
||||||
pub mod schema;
|
pub mod schema;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
pub mod version;
|
pub mod version;
|
||||||
|
pub mod webfinger;
|
||||||
pub mod websocket;
|
pub mod websocket;
|
||||||
|
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
|
|
|
@ -11,6 +11,7 @@ use lemmy_server::db::establish_connection;
|
||||||
use lemmy_server::feeds;
|
use lemmy_server::feeds;
|
||||||
use lemmy_server::nodeinfo;
|
use lemmy_server::nodeinfo;
|
||||||
use lemmy_server::settings::Settings;
|
use lemmy_server::settings::Settings;
|
||||||
|
use lemmy_server::webfinger;
|
||||||
use lemmy_server::websocket::server::*;
|
use lemmy_server::websocket::server::*;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
@ -255,7 +256,10 @@ fn main() {
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/federation/u/{user_name}",
|
"/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))
|
.bind((settings.bind, settings.port))
|
||||||
|
|
|
@ -56,7 +56,6 @@ lazy_static! {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Settings {
|
impl Settings {
|
||||||
|
|
||||||
/// Reads config from the files and environment.
|
/// Reads config from the files and environment.
|
||||||
/// First, defaults are loaded from CONFIG_FILE_DEFAULTS, then these values can be overwritten
|
/// 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
|
/// from CONFIG_FILE (optional). Finally, values from the environment (with prefix LEMMY) are
|
||||||
|
|
69
server/src/webfinger.rs
Normal file
69
server/src/webfinger.rs
Normal file
|
@ -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());
|
||||||
|
}
|
Loading…
Reference in a new issue