Use the webfinger crate

This commit is contained in:
Bat 2018-06-18 22:50:40 +01:00
parent 0dfc303c83
commit 5415b70854
6 changed files with 97 additions and 82 deletions

13
Cargo.lock generated
View File

@ -1020,6 +1020,7 @@ dependencies = [
"serde_json 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "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]] [[package]]
@ -1913,6 +1914,17 @@ name = "void"
version = "1.0.2" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "winapi" name = "winapi"
version = "0.2.8" 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 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 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 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.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 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" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"

View File

@ -24,6 +24,7 @@ serde_derive = "1.0"
serde_json = "1.0" serde_json = "1.0"
tera = "0.11" tera = "0.11"
url = "1.7" url = "1.7"
webfinger = "0.1"
[dependencies.chrono] [dependencies.chrono]
features = ["serde"] features = ["serde"]

View File

@ -33,6 +33,7 @@ extern crate serde_derive;
extern crate serde_json; extern crate serde_json;
extern crate tera; extern crate tera;
extern crate url; extern crate url;
extern crate webfinger;
use diesel::{pg::PgConnection, r2d2::{ConnectionManager, Pool}}; use diesel::{pg::PgConnection, r2d2::{ConnectionManager, Pool}};
use dotenv::dotenv; use dotenv::dotenv;

View File

@ -14,13 +14,13 @@ use openssl::{
rsa::Rsa, rsa::Rsa,
sign::Signer sign::Signer
}; };
use webfinger::*;
use activity_pub::{ use activity_pub::{
ActivityStream, Id, IntoId, ActivityStream, Id, IntoId,
actor::{Actor as APActor, ActorType}, actor::{Actor as APActor, ActorType},
inbox::WithInbox, inbox::WithInbox,
sign, sign
webfinger::*
}; };
use models::instance::*; use models::instance::*;
use schema::blogs; use schema::blogs;
@ -91,9 +91,9 @@ impl Blog {
fn fetch_from_webfinger(conn: &PgConnection, acct: String) -> Option<Blog> { fn fetch_from_webfinger(conn: &PgConnection, acct: String) -> Option<Blog> {
match resolve(acct.clone()) { 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) => { Err(details) => {
println!("{}", details); println!("{:?}", details);
None None
} }
} }
@ -171,6 +171,30 @@ impl Blog {
pub fn get_keypair(&self) -> PKey<Private> { pub fn get_keypair(&self) -> PKey<Private> {
PKey::from_rsa(Rsa::private_key_from_pem(self.private_key.clone().unwrap().as_ref()).unwrap()).unwrap() 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 { 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<String> {
vec![self.compute_id(conn)]
}
fn webfinger_links(&self, conn: &PgConnection) -> Vec<Vec<(String, String)>> {
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 { impl sign::Signer for Blog {
fn get_key_id(&self, conn: &PgConnection) -> String { fn get_key_id(&self, conn: &PgConnection) -> String {
format!("{}#main-key", self.compute_id(conn)) format!("{}#main-key", self.compute_id(conn))

View File

@ -24,6 +24,7 @@ use rocket::{
}; };
use serde_json; use serde_json;
use url::Url; use url::Url;
use webfinger::*;
use BASE_URL; use BASE_URL;
use activity_pub::{ use activity_pub::{
@ -31,7 +32,7 @@ use activity_pub::{
actor::{ActorType, Actor as APActor}, actor::{ActorType, Actor as APActor},
inbox::{Inbox, WithInbox}, inbox::{Inbox, WithInbox},
sign::{Signer, gen_keypair}, sign::{Signer, gen_keypair},
webfinger::{Webfinger, resolve} webfinger::{resolve}
}; };
use db_conn::DbConn; use db_conn::DbConn;
use models::{ use models::{
@ -336,6 +337,30 @@ impl User {
json["fqn"] = serde_json::Value::String(self.get_fqn(conn)); json["fqn"] = serde_json::Value::String(self.get_fqn(conn));
json 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 { 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<String> {
vec![self.compute_id(conn)]
}
fn webfinger_links(&self, conn: &PgConnection) -> Vec<Vec<(String, String)>> {
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 { impl Signer for User {
fn get_key_id(&self, conn: &PgConnection) -> String { fn get_key_id(&self, conn: &PgConnection) -> String {
format!("{}#main-key", self.compute_id(conn)) format!("{}#main-key", self.compute_id(conn))

View File

@ -1,8 +1,10 @@
use rocket::http::ContentType; use rocket::http::ContentType;
use rocket::response::Content; use rocket::response::Content;
use serde_json;
use webfinger::*;
use BASE_URL; use BASE_URL;
use activity_pub::{ap_url, webfinger::Webfinger}; use activity_pub::ap_url;
use db_conn::DbConn; use db_conn::DbConn;
use models::{blogs::Blog, users::User}; use models::{blogs::Blog, users::User};
@ -33,29 +35,32 @@ struct WebfingerQuery {
resource: String resource: String
} }
#[get("/.well-known/webfinger?<query>")] struct WebfingerResolver;
fn webfinger(query: WebfingerQuery, conn: DbConn) -> Content<Result<String, &'static str>> {
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();
if res_dom == BASE_URL.as_str() { impl Resolver<DbConn> for WebfingerResolver {
let res = match User::find_local(&*conn, String::from(user)) { fn instance_domain<'a>() -> &'a str {
BASE_URL.as_str()
}
fn find(acct: String, conn: DbConn) -> Result<Webfinger, ResolverError> {
match User::find_local(&*conn, acct.clone()) {
Some(usr) => Ok(usr.webfinger(&*conn)), Some(usr) => Ok(usr.webfinger(&*conn)),
None => match Blog::find_local(&*conn, String::from(user)) { None => match Blog::find_local(&*conn, acct) {
Some(blog) => Ok(blog.webfinger(&*conn)), Some(blog) => Ok(blog.webfinger(&*conn)),
None => Err("Requested actor not found") None => Err(ResolverError::NotFound)
} }
}; }
Content(ContentType::new("application", "jrd+json"), res) }
} else { }
Content(ContentType::new("text", "plain"), Err("Invalid instance"))
} #[get("/.well-known/webfinger?<query>")]
} else { fn webfinger(query: WebfingerQuery, conn: DbConn) -> Content<String> {
Content(ContentType::new("text", "plain"), Err("Invalid resource type. Only acct is supported")) 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"
}))
} }
} }