actix-webfinger/src/lib.rs
2019-01-27 14:45:44 -06:00

121 lines
4.2 KiB
Rust

use actix::Addr;
use actix_web::{
client::{self, ClientConnector},
HttpMessage,
};
use futures::{future::IntoFuture, Future};
use serde_derive::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Link {
pub rel: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub href: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub template: Option<String>,
#[serde(rename = "type")]
#[serde(skip_serializing_if = "Option::is_none")]
pub kind: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Webfinger {
pub aliases: Vec<String>,
pub links: Vec<Link>,
pub subject: String,
}
impl Webfinger {
pub fn aliases(&self) -> &[String] {
&self.aliases
}
pub fn links(&self) -> &[Link] {
&self.links
}
pub fn activitypub(&self) -> Option<&Link> {
self.links.iter().find(|l| {
l.rel == "self"
&& l.kind
.as_ref()
.map(|k| k == "application/activity+json")
.unwrap_or(false)
})
}
pub fn profile(&self) -> Option<&Link> {
self.links
.iter()
.find(|l| l.rel == "http://webfinger.net/rel/profile-page")
}
pub fn atom(&self) -> Option<&Link> {
self.links
.iter()
.find(|l| l.rel == "http://schemas.google.com/g/2010#updates-from")
}
pub fn salmon(&self) -> Option<&Link> {
self.links.iter().find(|l| l.rel == "salmon")
}
pub fn magic_public_key(&self) -> Option<&Link> {
self.links.iter().find(|l| l.rel == "magic-public-key")
}
pub fn ostatus(&self) -> Option<&Link> {
self.links
.iter()
.find(|l| l.rel == "http://ostatus.org/schema/1.0/subscribe")
}
pub fn fetch(
conn: Addr<ClientConnector>,
user: &str,
domain: &str,
) -> Box<Future<Item = Self, Error = actix_web::Error>> {
let url = format!(
"https://{}/.well-known/webfinger?resource=acct:{}",
domain, user
);
let fut = client::get(url)
.with_connector(conn)
.header("Accept", "application/json")
.finish()
.into_future()
.and_then(|r| {
r.send()
.from_err()
.and_then(|res| res.json::<Webfinger>().from_err())
});
Box::new(fut)
}
}
#[cfg(test)]
mod tests {
use crate::Webfinger;
const SIR_BOOPS: &'static str = r#"{"subject":"acct:Sir_Boops@sergal.org","aliases":["https://mastodon.sergal.org/@Sir_Boops","https://mastodon.sergal.org/users/Sir_Boops"],"links":[{"rel":"http://webfinger.net/rel/profile-page","type":"text/html","href":"https://mastodon.sergal.org/@Sir_Boops"},{"rel":"http://schemas.google.com/g/2010#updates-from","type":"application/atom+xml","href":"https://mastodon.sergal.org/users/Sir_Boops.atom"},{"rel":"self","type":"application/activity+json","href":"https://mastodon.sergal.org/users/Sir_Boops"},{"rel":"salmon","href":"https://mastodon.sergal.org/api/salmon/1"},{"rel":"magic-public-key","href":"data:application/magic-public-key,RSA.vwDujxmxoYHs64MyVB3LG5ZyBxV3ufaMRBFu42bkcTpISq1WwZ-3Zb6CI8zOO-nM-Q2llrVRYjZa4ZFnOLvMTq_Kf-Zf5wy2aCRer88gX-MsJOAtItSi412y0a_rKOuFaDYLOLeTkRvmGLgZWbsrZJOp-YWb3zQ5qsIOInkc5BwI172tMsGeFtsnbNApPV4lrmtTGaJ8RiM8MR7XANBOfOHggSt1-eAIKGIsCmINEMzs1mG9D75xKtC_sM8GfbvBclQcBstGkHAEj1VHPW0ch6Bok5_QQppicyb8UA1PAA9bznSFtKlYE4xCH8rlCDSDTBRtdnBWHKcj619Ujz4Qaw==.AQAB"},{"rel":"http://ostatus.org/schema/1.0/subscribe","template":"https://mastodon.sergal.org/authorize_interaction?uri={uri}"}]}"#;
#[test]
fn can_deserialize_sir_boops() {
let webfinger: Result<Webfinger, _> = serde_json::from_str(SIR_BOOPS);
assert!(webfinger.is_ok());
let webfinger = webfinger.unwrap();
assert!(webfinger.salmon().is_some());
assert!(webfinger.ostatus().is_some());
assert!(webfinger.activitypub().is_some());
assert!(webfinger.atom().is_some());
assert!(webfinger.magic_public_key().is_some());
assert!(webfinger.profile().is_some());
}
}