From 5415b70854dac7030cb5d827a41f00eafef324f4 Mon Sep 17 00:00:00 2001 From: Bat Date: Mon, 18 Jun 2018 22:50:40 +0100 Subject: [PATCH] Use the webfinger crate --- Cargo.lock | 13 +++++++++ Cargo.toml | 1 + src/main.rs | 1 + src/models/blogs.rs | 59 +++++++++++++++++++--------------------- src/models/users.rs | 54 ++++++++++++++++++------------------ src/routes/well_known.rs | 51 ++++++++++++++++++---------------- 6 files changed, 97 insertions(+), 82 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 272d796f..1708ea54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1020,6 +1020,7 @@ dependencies = [ "serde_json 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", "tera 0.11.7 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "webfinger 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1913,6 +1914,17 @@ name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "webfinger" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "reqwest 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.43 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "winapi" version = "0.2.8" @@ -2176,6 +2188,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +"checksum webfinger 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "27a4e6d1de7050af8beb026c02bcef5340ec1f3af6d4a02248b7990908baa3ff" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/Cargo.toml b/Cargo.toml index 27ebfa1a..87c2e879 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ serde_derive = "1.0" serde_json = "1.0" tera = "0.11" url = "1.7" +webfinger = "0.1" [dependencies.chrono] features = ["serde"] diff --git a/src/main.rs b/src/main.rs index 7a5fac5a..5455d854 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,6 +33,7 @@ extern crate serde_derive; extern crate serde_json; extern crate tera; extern crate url; +extern crate webfinger; use diesel::{pg::PgConnection, r2d2::{ConnectionManager, Pool}}; use dotenv::dotenv; diff --git a/src/models/blogs.rs b/src/models/blogs.rs index 529bd43d..dc779f6e 100644 --- a/src/models/blogs.rs +++ b/src/models/blogs.rs @@ -14,13 +14,13 @@ use openssl::{ rsa::Rsa, sign::Signer }; +use webfinger::*; use activity_pub::{ ActivityStream, Id, IntoId, actor::{Actor as APActor, ActorType}, inbox::WithInbox, - sign, - webfinger::* + sign }; use models::instance::*; use schema::blogs; @@ -91,9 +91,9 @@ impl Blog { fn fetch_from_webfinger(conn: &PgConnection, acct: String) -> Option { match resolve(acct.clone()) { - Ok(url) => Blog::fetch_from_url(conn, url), + Ok(wf) => wf.links.into_iter().find(|l| l.mime_type == Some(String::from("application/activity+json"))).and_then(|l| Blog::fetch_from_url(conn, l.href)), Err(details) => { - println!("{}", details); + println!("{:?}", details); None } } @@ -171,6 +171,30 @@ impl Blog { pub fn get_keypair(&self) -> PKey { PKey::from_rsa(Rsa::private_key_from_pem(self.private_key.clone().unwrap().as_ref()).unwrap()).unwrap() } + + pub fn webfinger(&self, conn: &PgConnection) -> Webfinger { + Webfinger { + subject: format!("acct:{}@{}", self.actor_id, self.get_instance(conn).public_domain), + aliases: vec![self.compute_id(conn)], + links: vec![ + Link { + rel: String::from("http://webfinger.net/rel/profile-page"), + mime_type: None, + href: self.compute_id(conn) + }, + Link { + rel: String::from("http://schemas.google.com/g/2010#updates-from"), + mime_type: Some(String::from("application/atom+xml")), + href: self.compute_box(conn, "feed.atom") + }, + Link { + rel: String::from("self"), + mime_type: Some(String::from("application/activity+json")), + href: self.compute_id(conn) + } + ] + } + } } impl IntoId for Blog { @@ -234,33 +258,6 @@ impl APActor for Blog { } } -impl Webfinger for Blog { - fn webfinger_subject(&self, conn: &PgConnection) -> String { - format!("acct:{}@{}", self.actor_id, self.get_instance(conn).public_domain) - } - fn webfinger_aliases(&self, conn: &PgConnection) -> Vec { - vec![self.compute_id(conn)] - } - fn webfinger_links(&self, conn: &PgConnection) -> Vec> { - vec![ - vec![ - (String::from("rel"), String::from("http://webfinger.net/rel/profile-page")), - (String::from("href"), self.compute_id(conn)) - ], - vec![ - (String::from("rel"), String::from("http://schemas.google.com/g/2010#updates-from")), - (String::from("type"), String::from("application/atom+xml")), - (String::from("href"), self.compute_box(conn, "feed.atom")) - ], - vec![ - (String::from("rel"), String::from("self")), - (String::from("type"), String::from("application/activity+json")), - (String::from("href"), self.compute_id(conn)) - ] - ] - } -} - impl sign::Signer for Blog { fn get_key_id(&self, conn: &PgConnection) -> String { format!("{}#main-key", self.compute_id(conn)) diff --git a/src/models/users.rs b/src/models/users.rs index dc37ff95..e2543117 100644 --- a/src/models/users.rs +++ b/src/models/users.rs @@ -24,6 +24,7 @@ use rocket::{ }; use serde_json; use url::Url; +use webfinger::*; use BASE_URL; use activity_pub::{ @@ -31,7 +32,7 @@ use activity_pub::{ actor::{ActorType, Actor as APActor}, inbox::{Inbox, WithInbox}, sign::{Signer, gen_keypair}, - webfinger::{Webfinger, resolve} + webfinger::{resolve} }; use db_conn::DbConn; use models::{ @@ -336,6 +337,30 @@ impl User { json["fqn"] = serde_json::Value::String(self.get_fqn(conn)); json } + + pub fn webfinger(&self, conn: &PgConnection) -> Webfinger { + Webfinger { + subject: format!("acct:{}@{}", self.username, self.get_instance(conn).public_domain), + aliases: vec![self.compute_id(conn)], + links: vec![ + Link { + rel: String::from("http://webfinger.net/rel/profile-page"), + mime_type: None, + href: self.compute_id(conn) + }, + Link { + rel: String::from("http://schemas.google.com/g/2010#updates-from"), + mime_type: Some(String::from("application/atom+xml")), + href: self.compute_box(conn, "feed.atom") + }, + Link { + rel: String::from("self"), + mime_type: Some(String::from("application/activity+json")), + href: self.compute_id(conn) + } + ] + } + } } impl<'a, 'r> FromRequest<'a, 'r> for User { @@ -445,33 +470,6 @@ impl Inbox for User { } } -impl Webfinger for User { - fn webfinger_subject(&self, conn: &PgConnection) -> String { - format!("acct:{}@{}", self.username, self.get_instance(conn).public_domain) - } - fn webfinger_aliases(&self, conn: &PgConnection) -> Vec { - vec![self.compute_id(conn)] - } - fn webfinger_links(&self, conn: &PgConnection) -> Vec> { - vec![ - vec![ - (String::from("rel"), String::from("http://webfinger.net/rel/profile-page")), - (String::from("href"), self.compute_id(conn)) - ], - vec![ - (String::from("rel"), String::from("http://schemas.google.com/g/2010#updates-from")), - (String::from("type"), String::from("application/atom+xml")), - (String::from("href"), self.compute_box(conn, "feed.atom")) - ], - vec![ - (String::from("rel"), String::from("self")), - (String::from("type"), String::from("application/activity+json")), - (String::from("href"), self.compute_id(conn)) - ] - ] - } -} - impl Signer for User { fn get_key_id(&self, conn: &PgConnection) -> String { format!("{}#main-key", self.compute_id(conn)) diff --git a/src/routes/well_known.rs b/src/routes/well_known.rs index 15b3ad2a..ec9710a2 100644 --- a/src/routes/well_known.rs +++ b/src/routes/well_known.rs @@ -1,8 +1,10 @@ use rocket::http::ContentType; use rocket::response::Content; +use serde_json; +use webfinger::*; use BASE_URL; -use activity_pub::{ap_url, webfinger::Webfinger}; +use activity_pub::ap_url; use db_conn::DbConn; use models::{blogs::Blog, users::User}; @@ -33,29 +35,32 @@ struct WebfingerQuery { resource: String } -#[get("/.well-known/webfinger?")] -fn webfinger(query: WebfingerQuery, conn: DbConn) -> Content> { - let mut parsed_query = query.resource.splitn(2, ":"); - let res_type = parsed_query.next().unwrap(); - let res = parsed_query.next().unwrap(); - if res_type == "acct" { - let mut parsed_res = res.split("@"); - let user = parsed_res.next().unwrap(); - let res_dom = parsed_res.next().unwrap(); +struct WebfingerResolver; - if res_dom == BASE_URL.as_str() { - let res = match User::find_local(&*conn, String::from(user)) { - Some(usr) => Ok(usr.webfinger(&*conn)), - None => match Blog::find_local(&*conn, String::from(user)) { - Some(blog) => Ok(blog.webfinger(&*conn)), - None => Err("Requested actor not found") - } - }; - Content(ContentType::new("application", "jrd+json"), res) - } else { - Content(ContentType::new("text", "plain"), Err("Invalid instance")) +impl Resolver for WebfingerResolver { + fn instance_domain<'a>() -> &'a str { + BASE_URL.as_str() + } + + fn find(acct: String, conn: DbConn) -> Result { + match User::find_local(&*conn, acct.clone()) { + Some(usr) => Ok(usr.webfinger(&*conn)), + None => match Blog::find_local(&*conn, acct) { + Some(blog) => Ok(blog.webfinger(&*conn)), + None => Err(ResolverError::NotFound) + } } - } else { - Content(ContentType::new("text", "plain"), Err("Invalid resource type. Only acct is supported")) + } +} + +#[get("/.well-known/webfinger?")] +fn webfinger(query: WebfingerQuery, conn: DbConn) -> Content { + match WebfingerResolver::endpoint(query.resource, conn).and_then(|wf| serde_json::to_string(&wf).map_err(|_| ResolverError::NotFound)) { + Ok(wf) => Content(ContentType::new("application", "jrd+json"), wf), + Err(err) => Content(ContentType::new("text", "plain"), String::from(match err { + ResolverError::InvalidResource => "Invalid resource. Make sure to request an acct: URI", + ResolverError::NotFound => "Requested resource was not found", + ResolverError::WrongInstance => "This is not the instance of the requested resource" + })) } }