From 3a448e9e1707baf245c802de13a9d678bf0589d4 Mon Sep 17 00:00:00 2001 From: Kitaiti Makoto Date: Fri, 24 Sep 2021 04:27:22 +0900 Subject: [PATCH] Sign GET request to external instances --- plume-common/src/activity_pub/inbox.rs | 162 +++++++++++++++++++---- plume-common/src/activity_pub/request.rs | 20 +-- plume-common/src/activity_pub/sign.rs | 17 ++- plume-models/src/blogs.rs | 42 ++++-- plume-models/src/comments.rs | 9 +- plume-models/src/follows.rs | 6 +- plume-models/src/inbox.rs | 36 ++--- plume-models/src/instance.rs | 14 +- plume-models/src/lib.rs | 14 +- plume-models/src/likes.rs | 6 +- plume-models/src/medias.rs | 1 + plume-models/src/posts.rs | 29 +++- plume-models/src/remote_fetch_actor.rs | 9 +- plume-models/src/reshares.rs | 6 +- plume-models/src/users.rs | 21 +-- src/inbox.rs | 10 +- src/routes/instance.rs | 16 ++- 17 files changed, 321 insertions(+), 97 deletions(-) diff --git a/plume-common/src/activity_pub/inbox.rs b/plume-common/src/activity_pub/inbox.rs index 5b934136..716668ad 100644 --- a/plume-common/src/activity_pub/inbox.rs +++ b/plume-common/src/activity_pub/inbox.rs @@ -1,6 +1,10 @@ use std::fmt::Debug; -use super::request; +use super::{request, sign::Signer}; +use reqwest::{ + header::{HeaderValue, HOST}, + Url, +}; /// Represents an ActivityPub inbox. /// @@ -11,7 +15,8 @@ use super::request; /// ```rust /// # extern crate activitypub; /// # use activitypub::{actor::Person, activity::{Announce, Create}, object::Note}; -/// # use plume_common::activity_pub::inbox::*; +/// # use openssl::{hash::MessageDigest, pkey::PKey, rsa::Rsa}; +/// # use plume_common::activity_pub::{inbox::*, sign::{gen_keypair, Error as SignatureError, Result as SignatureResult, Signer}}; /// # struct User; /// # impl FromId<()> for User { /// # type Error = (); @@ -60,6 +65,42 @@ use super::request; /// # Ok(()) /// # } /// # } +/// # struct MySigner { +/// # public_key: String, +/// # private_key: String, +/// # } +/// # +/// # impl MySigner { +/// # fn new() -> Self { +/// # let (pub_key, priv_key) = gen_keypair(); +/// # Self { +/// # public_key: String::from_utf8(pub_key).unwrap(), +/// # private_key: String::from_utf8(priv_key).unwrap(), +/// # } +/// # } +/// # } +/// # +/// # impl Signer for MySigner { +/// # fn get_key_id(&self) -> String { +/// # "mysigner".into() +/// # } +/// # +/// # fn sign(&self, to_sign: &str) -> SignatureResult> { +/// # let key = PKey::from_rsa(Rsa::private_key_from_pem(self.private_key.as_ref()).unwrap()) +/// # .unwrap(); +/// # let mut signer = openssl::sign::Signer::new(MessageDigest::sha256(), &key).unwrap(); +/// # signer.update(to_sign.as_bytes()).unwrap(); +/// # signer.sign_to_vec().map_err(|_| SignatureError()) +/// # } +/// # +/// # fn verify(&self, data: &str, signature: &[u8]) -> SignatureResult { +/// # let key = PKey::from_rsa(Rsa::public_key_from_pem(self.public_key.as_ref()).unwrap()) +/// # .unwrap(); +/// # let mut verifier = openssl::sign::Verifier::new(MessageDigest::sha256(), &key).unwrap(); +/// # verifier.update(data.as_bytes()).unwrap(); +/// # verifier.verify(&signature).map_err(|_| SignatureError()) +/// # } +/// # } /// # /// # let mut act = Create::default(); /// # act.object_props.set_id_string(String::from("https://test.ap/activity")).unwrap(); @@ -70,8 +111,9 @@ use super::request; /// # let activity_json = serde_json::to_value(act).unwrap(); /// # /// # let conn = (); +/// # let sender = MySigner::new(); /// # -/// let result: Result<(), ()> = Inbox::handle(&conn, activity_json) +/// let result: Result<(), ()> = Inbox::handle(&conn, &sender, activity_json) /// .with::(None) /// .with::(None) /// .done(); @@ -85,9 +127,10 @@ where /// # Structure /// /// - the context to be passed to each handler. + /// - the sender actor to sign request /// - the activity /// - the reason it has not been handled yet - NotHandled(&'a C, serde_json::Value, InboxError), + NotHandled(&'a C, &'a dyn Signer, serde_json::Value, InboxError), /// A matching handler have been found but failed /// @@ -140,8 +183,12 @@ where /// /// - `ctx`: the context to pass to each handler /// - `json`: the JSON representation of the incoming activity - pub fn handle(ctx: &'a C, json: serde_json::Value) -> Inbox<'a, C, E, R> { - Inbox::NotHandled(ctx, json, InboxError::NoMatch) + pub fn handle( + ctx: &'a C, + sender: &'a dyn Signer, + json: serde_json::Value, + ) -> Inbox<'a, C, E, R> { + Inbox::NotHandled(ctx, sender, json, InboxError::NoMatch) } /// Registers an handler on this Inbox. @@ -152,27 +199,30 @@ where M: AsObject + FromId, M::Output: Into, { - if let Inbox::NotHandled(ctx, mut act, e) = self { + if let Inbox::NotHandled(ctx, sender, mut act, e) = self { if serde_json::from_value::(act.clone()).is_ok() { let act_clone = act.clone(); let act_id = match act_clone["id"].as_str() { Some(x) => x, - None => return Inbox::NotHandled(ctx, act, InboxError::InvalidID), + None => return Inbox::NotHandled(ctx, sender, act, InboxError::InvalidID), }; // Get the actor ID let actor_id = match get_id(act["actor"].clone()) { Some(x) => x, - None => return Inbox::NotHandled(ctx, act, InboxError::InvalidActor(None)), + None => { + return Inbox::NotHandled(ctx, sender, act, InboxError::InvalidActor(None)) + } }; if Self::is_spoofed_activity(&actor_id, &act) { - return Inbox::NotHandled(ctx, act, InboxError::InvalidObject(None)); + return Inbox::NotHandled(ctx, sender, act, InboxError::InvalidObject(None)); } // Transform this actor to a model (see FromId for details about the from_id function) let actor = match A::from_id( ctx, + sender, &actor_id, serde_json::from_value(act["actor"].clone()).ok(), proxy, @@ -183,17 +233,25 @@ where if let Some(json) = json { act["actor"] = json; } - return Inbox::NotHandled(ctx, act, InboxError::InvalidActor(Some(e))); + return Inbox::NotHandled( + ctx, + sender, + act, + InboxError::InvalidActor(Some(e)), + ); } }; // Same logic for "object" let obj_id = match get_id(act["object"].clone()) { Some(x) => x, - None => return Inbox::NotHandled(ctx, act, InboxError::InvalidObject(None)), + None => { + return Inbox::NotHandled(ctx, sender, act, InboxError::InvalidObject(None)) + } }; let obj = match M::from_id( ctx, + sender, &obj_id, serde_json::from_value(act["object"].clone()).ok(), proxy, @@ -203,7 +261,12 @@ where if let Some(json) = json { act["object"] = json; } - return Inbox::NotHandled(ctx, act, InboxError::InvalidObject(Some(e))); + return Inbox::NotHandled( + ctx, + sender, + act, + InboxError::InvalidObject(Some(e)), + ); } }; @@ -215,7 +278,7 @@ where } else { // If the Activity type is not matching the expected one for // this handler, try with the next one. - Inbox::NotHandled(ctx, act, e) + Inbox::NotHandled(ctx, sender, act, e) } } else { self @@ -226,7 +289,7 @@ where pub fn done(self) -> Result { match self { Inbox::Handled(res) => Ok(res), - Inbox::NotHandled(_, _, err) => Err(E::from(err)), + Inbox::NotHandled(_, _, _, err) => Err(E::from(err)), Inbox::Failed(err) => Err(err), } } @@ -293,6 +356,7 @@ pub trait FromId: Sized { /// If absent, the ID will be dereferenced. fn from_id( ctx: &C, + sender: &dyn Signer, id: &str, object: Option, proxy: Option<&reqwest::Proxy>, @@ -301,7 +365,7 @@ pub trait FromId: Sized { Ok(x) => Ok(x), _ => match object { Some(o) => Self::from_activity(ctx, o).map_err(|e| (None, e)), - None => Self::from_activity(ctx, Self::deref(id, proxy.cloned())?) + None => Self::from_activity(ctx, Self::deref(id, sender, proxy.cloned())?) .map_err(|e| (None, e)), }, } @@ -310,9 +374,17 @@ pub trait FromId: Sized { /// Dereferences an ID fn deref( id: &str, + sender: &dyn Signer, proxy: Option, ) -> Result, Self::Error)> { - let headers = request::headers(); + let mut headers = request::headers(); + let url = Url::parse(&id).map_err(|_| (None, InboxError::InvalidID.into()))?; + if !url.has_host() { + return Err((None, InboxError::InvalidID.into())); + } + let host_header_value = HeaderValue::from_str(&url.host_str().expect("Unreachable")) + .map_err(|_| (None, InboxError::DerefError.into()))?; + headers.insert(HOST, host_header_value); if let Some(proxy) = proxy { reqwest::ClientBuilder::new().proxy(proxy) } else { @@ -322,7 +394,12 @@ pub trait FromId: Sized { .build() .map_err(|_| (None, InboxError::DerefError.into()))? .get(id) - .headers(headers) + .headers(headers.clone()) + .header( + "Signature", + request::signature(sender, &headers, ("get", url.path(), url.query())) + .map_err(|_| (None, InboxError::DerefError.into()))?, + ) .send() .map_err(|_| (None, InboxError::DerefError)) .and_then(|mut r| { @@ -451,8 +528,10 @@ where #[cfg(test)] mod tests { + use super::super::sign::{gen_keypair, Error as SignatureError, Result as SignatureResult}; use super::*; use activitypub::{activity::*, actor::Person, object::Note}; + use openssl::{hash::MessageDigest, pkey::PKey, rsa::Rsa}; struct MyActor; impl FromId<()> for MyActor { @@ -550,10 +629,47 @@ mod tests { act } + struct MySigner { + public_key: String, + private_key: String, + } + + impl MySigner { + fn new() -> Self { + let (pub_key, priv_key) = gen_keypair(); + Self { + public_key: String::from_utf8(pub_key).unwrap(), + private_key: String::from_utf8(priv_key).unwrap(), + } + } + } + + impl Signer for MySigner { + fn get_key_id(&self) -> String { + "mysigner".into() + } + + fn sign(&self, to_sign: &str) -> SignatureResult> { + let key = PKey::from_rsa(Rsa::private_key_from_pem(self.private_key.as_ref()).unwrap()) + .unwrap(); + let mut signer = openssl::sign::Signer::new(MessageDigest::sha256(), &key).unwrap(); + signer.update(to_sign.as_bytes()).unwrap(); + signer.sign_to_vec().map_err(|_| SignatureError()) + } + + fn verify(&self, data: &str, signature: &[u8]) -> SignatureResult { + let key = PKey::from_rsa(Rsa::public_key_from_pem(self.public_key.as_ref()).unwrap()) + .unwrap(); + let mut verifier = openssl::sign::Verifier::new(MessageDigest::sha256(), &key).unwrap(); + verifier.update(data.as_bytes()).unwrap(); + verifier.verify(&signature).map_err(|_| SignatureError()) + } + } + #[test] fn test_inbox_basic() { let act = serde_json::to_value(build_create()).unwrap(); - let res: Result<(), ()> = Inbox::handle(&(), act) + let res: Result<(), ()> = Inbox::handle(&(), &MySigner::new(), act) .with::(None) .done(); assert!(res.is_ok()); @@ -562,7 +678,7 @@ mod tests { #[test] fn test_inbox_multi_handlers() { let act = serde_json::to_value(build_create()).unwrap(); - let res: Result<(), ()> = Inbox::handle(&(), act) + let res: Result<(), ()> = Inbox::handle(&(), &MySigner::new(), act) .with::(None) .with::(None) .with::(None) @@ -575,7 +691,7 @@ mod tests { fn test_inbox_failure() { let act = serde_json::to_value(build_create()).unwrap(); // Create is not handled by this inbox - let res: Result<(), ()> = Inbox::handle(&(), act) + let res: Result<(), ()> = Inbox::handle(&(), &MySigner::new(), act) .with::(None) .with::(None) .done(); @@ -624,12 +740,12 @@ mod tests { fn test_inbox_actor_failure() { let act = serde_json::to_value(build_create()).unwrap(); - let res: Result<(), ()> = Inbox::handle(&(), act.clone()) + let res: Result<(), ()> = Inbox::handle(&(), &MySigner::new(), act.clone()) .with::(None) .done(); assert!(res.is_err()); - let res: Result<(), ()> = Inbox::handle(&(), act.clone()) + let res: Result<(), ()> = Inbox::handle(&(), &MySigner::new(), act.clone()) .with::(None) .with::(None) .done(); diff --git a/plume-common/src/activity_pub/request.rs b/plume-common/src/activity_pub/request.rs index 73e6d6ee..f3a0efc3 100644 --- a/plume-common/src/activity_pub/request.rs +++ b/plume-common/src/activity_pub/request.rs @@ -118,8 +118,8 @@ type Path<'a> = &'a str; type Query<'a> = &'a str; type RequestTarget<'a> = (Method<'a>, Path<'a>, Option>); -pub fn signature( - signer: &S, +pub fn signature( + signer: &dyn Signer, headers: &HeaderMap, request_target: RequestTarget, ) -> Result { @@ -166,8 +166,10 @@ pub fn signature( #[cfg(test)] mod tests { - use super::{signature, Error}; - use crate::activity_pub::sign::{gen_keypair, Signer}; + use super::signature; + use crate::activity_pub::sign::{ + gen_keypair, Error as SignatureError, Result as SignatureResult, Signer, + }; use openssl::{hash::MessageDigest, pkey::PKey, rsa::Rsa}; use reqwest::header::HeaderMap; @@ -187,26 +189,24 @@ mod tests { } impl Signer for MySigner { - type Error = Error; - fn get_key_id(&self) -> String { "mysigner".into() } - fn sign(&self, to_sign: &str) -> Result, Self::Error> { + fn sign(&self, to_sign: &str) -> SignatureResult> { let key = PKey::from_rsa(Rsa::private_key_from_pem(self.private_key.as_ref()).unwrap()) .unwrap(); let mut signer = openssl::sign::Signer::new(MessageDigest::sha256(), &key).unwrap(); signer.update(to_sign.as_bytes()).unwrap(); - signer.sign_to_vec().map_err(|_| Error()) + signer.sign_to_vec().map_err(|_| SignatureError()) } - fn verify(&self, data: &str, signature: &[u8]) -> Result { + fn verify(&self, data: &str, signature: &[u8]) -> SignatureResult { let key = PKey::from_rsa(Rsa::public_key_from_pem(self.public_key.as_ref()).unwrap()) .unwrap(); let mut verifier = openssl::sign::Verifier::new(MessageDigest::sha256(), &key).unwrap(); verifier.update(data.as_bytes()).unwrap(); - verifier.verify(&signature).map_err(|_| Error()) + verifier.verify(&signature).map_err(|_| SignatureError()) } } diff --git a/plume-common/src/activity_pub/sign.rs b/plume-common/src/activity_pub/sign.rs index a6bb5899..5408b710 100644 --- a/plume-common/src/activity_pub/sign.rs +++ b/plume-common/src/activity_pub/sign.rs @@ -19,20 +19,25 @@ pub fn gen_keypair() -> (Vec, Vec) { #[derive(Debug)] pub struct Error(); +pub type Result = std::result::Result; + +impl From for Error { + fn from(_: openssl::error::ErrorStack) -> Self { + Self() + } +} pub trait Signer { - type Error; - fn get_key_id(&self) -> String; /// Sign some data with the signer keypair - fn sign(&self, to_sign: &str) -> Result, Self::Error>; + fn sign(&self, to_sign: &str) -> Result>; /// Verify if the signature is valid - fn verify(&self, data: &str, signature: &[u8]) -> Result; + fn verify(&self, data: &str, signature: &[u8]) -> Result; } pub trait Signable { - fn sign(&mut self, creator: &T) -> Result<&mut Self, Error> + fn sign(&mut self, creator: &T) -> Result<&mut Self> where T: Signer; fn verify(self, creator: &T) -> bool @@ -46,7 +51,7 @@ pub trait Signable { } impl Signable for serde_json::Value { - fn sign(&mut self, creator: &T) -> Result<&mut serde_json::Value, Error> { + fn sign(&mut self, creator: &T) -> Result<&mut serde_json::Value> { let creation_date = Utc::now().to_rfc3339(); let mut options = json!({ "type": "RsaSignature2017", diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index 179cec02..ca53a3a4 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -18,7 +18,8 @@ use openssl::{ }; use plume_common::activity_pub::{ inbox::{AsActor, FromId}, - sign, ActivityStream, ApSignature, Id, IntoId, PublicKey, Source, + sign::{self, Error as SignatureError, Result as SignatureResult}, + ActivityStream, ApSignature, Id, IntoId, PublicKey, Source, }; use url::Url; use webfinger::*; @@ -149,7 +150,16 @@ impl Blog { .into_iter() .find(|l| l.mime_type == Some(String::from("application/activity+json"))) .ok_or(Error::Webfinger) - .and_then(|l| Blog::from_id(conn, &l.href?, None, CONFIG.proxy()).map_err(|(_, e)| e)) + .and_then(|l| { + Blog::from_id( + conn, + &Instance::get_local().expect("Failed to get local instance"), + &l.href?, + None, + CONFIG.proxy(), + ) + .map_err(|(_, e)| e) + }) } pub fn to_activity(&self, conn: &Connection) -> Result { @@ -374,7 +384,14 @@ impl FromId for Blog { Media::save_remote( conn, icon.object_props.url_string().ok()?, - &User::from_id(conn, &owner, None, CONFIG.proxy()).ok()?, + &User::from_id( + conn, + &Instance::get_local().expect("Failed to get local instance"), + &owner, + None, + CONFIG.proxy(), + ) + .ok()?, ) .ok() }) @@ -390,7 +407,14 @@ impl FromId for Blog { Media::save_remote( conn, banner.object_props.url_string().ok()?, - &User::from_id(conn, &owner, None, CONFIG.proxy()).ok()?, + &User::from_id( + conn, + &Instance::get_local().expect("Failed to get local instance"), + &owner, + None, + CONFIG.proxy(), + ) + .ok()?, ) .ok() }) @@ -453,24 +477,22 @@ impl AsActor<&PlumeRocket> for Blog { } impl sign::Signer for Blog { - type Error = Error; - fn get_key_id(&self) -> String { format!("{}#main-key", self.ap_url) } - fn sign(&self, to_sign: &str) -> Result> { + fn sign(&self, to_sign: &str) -> SignatureResult> { let key = self.get_keypair()?; let mut signer = Signer::new(MessageDigest::sha256(), &key)?; signer.update(to_sign.as_bytes())?; - signer.sign_to_vec().map_err(Error::from) + signer.sign_to_vec().map_err(SignatureError::from) } - fn verify(&self, data: &str, signature: &[u8]) -> Result { + fn verify(&self, data: &str, signature: &[u8]) -> SignatureResult { let key = PKey::from_rsa(Rsa::public_key_from_pem(self.public_key.as_ref())?)?; let mut verifier = Verifier::new(MessageDigest::sha256(), &key)?; verifier.update(data.as_bytes())?; - verifier.verify(&signature).map_err(Error::from) + verifier.verify(&signature).map_err(SignatureError::from) } } diff --git a/plume-models/src/comments.rs b/plume-models/src/comments.rs index 2a9cac2f..3182fab6 100644 --- a/plume-models/src/comments.rs +++ b/plume-models/src/comments.rs @@ -236,6 +236,7 @@ impl FromId for Comment { })?, author_id: User::from_id( conn, + &Instance::get_local().expect("Failed to get local instance"), ¬e.object_props.attributed_to_link::()?, None, CONFIG.proxy(), @@ -294,7 +295,13 @@ impl FromId for Comment { .collect::>() // remove duplicates (don't do a query more than once) .into_iter() .map(|v| { - if let Ok(user) = User::from_id(conn, &v, None, CONFIG.proxy()) { + if let Ok(user) = User::from_id( + conn, + &Instance::get_local().expect("Failed to get local instance"), + &v, + None, + CONFIG.proxy(), + ) { vec![user] } else { vec![] // TODO try to fetch collection diff --git a/plume-models/src/follows.rs b/plume-models/src/follows.rs index da465d44..7ac6907d 100644 --- a/plume-models/src/follows.rs +++ b/plume-models/src/follows.rs @@ -1,6 +1,6 @@ use crate::{ - ap_url, db_conn::DbConn, notifications::*, schema::follows, users::User, Connection, Error, - Result, CONFIG, + ap_url, db_conn::DbConn, instance::Instance, notifications::*, schema::follows, users::User, + Connection, Error, Result, CONFIG, }; use activitypub::activity::{Accept, Follow as FollowAct, Undo}; use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl}; @@ -168,6 +168,7 @@ impl FromId for Follow { fn from_activity(conn: &DbConn, follow: FollowAct) -> Result { let actor = User::from_id( conn, + &Instance::get_local().expect("Failed to get local instance"), &follow.follow_props.actor_link::()?, None, CONFIG.proxy(), @@ -176,6 +177,7 @@ impl FromId for Follow { let target = User::from_id( conn, + &Instance::get_local().expect("Failed to get local instance"), &follow.follow_props.object_link::()?, None, CONFIG.proxy(), diff --git a/plume-models/src/inbox.rs b/plume-models/src/inbox.rs index 21af9d88..1c17ee11 100644 --- a/plume-models/src/inbox.rs +++ b/plume-models/src/inbox.rs @@ -3,7 +3,9 @@ use activitypub::activity::*; use crate::{ comments::Comment, db_conn::DbConn, - follows, likes, + follows, + instance::Instance, + likes, posts::{Post, PostUpdate}, reshares::Reshare, users::User, @@ -47,20 +49,24 @@ impl_into_inbox_result! { } pub fn inbox(conn: &DbConn, act: serde_json::Value) -> Result { - Inbox::handle(conn, act) - .with::(CONFIG.proxy()) - .with::(CONFIG.proxy()) - .with::(CONFIG.proxy()) - .with::(CONFIG.proxy()) - .with::(CONFIG.proxy()) - .with::(CONFIG.proxy()) - .with::(CONFIG.proxy()) - .with::(CONFIG.proxy()) - .with::(CONFIG.proxy()) - .with::(CONFIG.proxy()) - .with::(CONFIG.proxy()) - .with::(CONFIG.proxy()) - .done() + Inbox::handle( + conn, + &Instance::get_local().expect("Failed to get local instance"), + act, + ) + .with::(CONFIG.proxy()) + .with::(CONFIG.proxy()) + .with::(CONFIG.proxy()) + .with::(CONFIG.proxy()) + .with::(CONFIG.proxy()) + .with::(CONFIG.proxy()) + .with::(CONFIG.proxy()) + .with::(CONFIG.proxy()) + .with::(CONFIG.proxy()) + .with::(CONFIG.proxy()) + .with::(CONFIG.proxy()) + .with::(CONFIG.proxy()) + .done() } #[cfg(test)] diff --git a/plume-models/src/instance.rs b/plume-models/src/instance.rs index ee30f291..68c85ebb 100644 --- a/plume-models/src/instance.rs +++ b/plume-models/src/instance.rs @@ -17,7 +17,7 @@ use openssl::{ }; use plume_common::{ activity_pub::{ - sign::{gen_keypair, Signer}, + sign::{gen_keypair, Error as SignatureError, Result as SignatureResult, Signer}, ApSignature, PublicKey, }, utils::md_to_html, @@ -348,30 +348,28 @@ impl NewInstance { } impl Signer for Instance { - type Error = Error; - fn get_key_id(&self) -> String { format!("{}#main-key", self.ap_url()) } - fn sign(&self, to_sign: &str) -> Result> { + fn sign(&self, to_sign: &str) -> SignatureResult> { let key = self.get_keypair()?; let mut signer = sign::Signer::new(MessageDigest::sha256(), &key)?; signer.update(to_sign.as_bytes())?; - signer.sign_to_vec().map_err(Error::from) + signer.sign_to_vec().map_err(SignatureError::from) } - fn verify(&self, data: &str, signature: &[u8]) -> Result { + fn verify(&self, data: &str, signature: &[u8]) -> SignatureResult { if self.public_key.is_none() { warn!("missing public key for {}", self.public_domain); - return Err(Error::Signature); + return Err(SignatureError()); } let key = PKey::from_rsa(Rsa::public_key_from_pem( self.public_key.clone().unwrap().as_ref(), )?)?; let mut verifier = sign::Verifier::new(MessageDigest::sha256(), &key)?; verifier.update(data.as_bytes())?; - verifier.verify(&signature).map_err(Error::from) + verifier.verify(&signature).map_err(SignatureError::from) } } diff --git a/plume-models/src/lib.rs b/plume-models/src/lib.rs index 63bef55b..72f5a7e9 100755 --- a/plume-models/src/lib.rs +++ b/plume-models/src/lib.rs @@ -20,7 +20,7 @@ extern crate tantivy; use db_conn::DbPool; use instance::Instance; use once_cell::sync::Lazy; -use plume_common::activity_pub::inbox::InboxError; +use plume_common::activity_pub::{inbox::InboxError, sign}; use posts::PostEvent; use riker::actors::{channel, ActorSystem, ChannelRef, SystemBuilder}; use users::UserEvent; @@ -82,6 +82,12 @@ impl From for Error { } } +impl From for Error { + fn from(_: sign::Error) -> Self { + Error::Signature + } +} + impl From for Error { fn from(err: diesel::result::Error) -> Self { Error::Db(err) @@ -162,6 +168,12 @@ impl From> for Error { pub type Result = std::result::Result; +impl From for sign::Error { + fn from(_: Error) -> Self { + Self() + } +} + /// Adds a function to a model, that returns the first /// matching row for a given list of fields. /// diff --git a/plume-models/src/likes.rs b/plume-models/src/likes.rs index 78d81b54..e047203c 100644 --- a/plume-models/src/likes.rs +++ b/plume-models/src/likes.rs @@ -1,6 +1,6 @@ use crate::{ - db_conn::DbConn, notifications::*, posts::Post, schema::likes, timeline::*, users::User, - Connection, Error, Result, CONFIG, + db_conn::DbConn, instance::Instance, notifications::*, posts::Post, schema::likes, timeline::*, + users::User, Connection, Error, Result, CONFIG, }; use activitypub::activity; use chrono::NaiveDateTime; @@ -117,6 +117,7 @@ impl FromId for Like { NewLike { post_id: Post::from_id( conn, + &Instance::get_local().expect("Failed to get local instance"), &act.like_props.object_link::()?, None, CONFIG.proxy(), @@ -125,6 +126,7 @@ impl FromId for Like { .id, user_id: User::from_id( conn, + &Instance::get_local().expect("Failed to get local instance"), &act.like_props.actor_link::()?, None, CONFIG.proxy(), diff --git a/plume-models/src/medias.rs b/plume-models/src/medias.rs index 13a212b6..3eb80f47 100644 --- a/plume-models/src/medias.rs +++ b/plume-models/src/medias.rs @@ -272,6 +272,7 @@ impl Media { content_warning: image.object_props.summary_string().ok(), owner_id: User::from_id( conn, + &Instance::get_local().expect("Failed to get local instance"), image .object_props .attributed_to_link_vec::() diff --git a/plume-models/src/posts.rs b/plume-models/src/posts.rs index afd251f8..f6dec154 100644 --- a/plume-models/src/posts.rs +++ b/plume-models/src/posts.rs @@ -630,13 +630,28 @@ impl FromId for Post { .into_iter() .fold((None, vec![]), |(blog, mut authors), link| { let url = link; - match User::from_id(conn, &url, None, CONFIG.proxy()) { + match User::from_id( + conn, + &Instance::get_local().expect("Failed to get local instance"), + &url, + None, + CONFIG.proxy(), + ) { Ok(u) => { authors.push(u); (blog, authors) } Err(_) => ( - blog.or_else(|| Blog::from_id(conn, &url, None, CONFIG.proxy()).ok()), + blog.or_else(|| { + Blog::from_id( + conn, + &Instance::get_local().expect("Failed to get local instance"), + &url, + None, + CONFIG.proxy(), + ) + .ok() + }), authors, ), } @@ -837,8 +852,14 @@ impl AsObject for PostUpdate { type Output = (); fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> { - let mut post = - Post::from_id(conn, &self.ap_url, None, CONFIG.proxy()).map_err(|(_, e)| e)?; + let mut post = Post::from_id( + conn, + &Instance::get_local().expect("Failed to get local instance"), + &self.ap_url, + None, + CONFIG.proxy(), + ) + .map_err(|(_, e)| e)?; if !post.is_author(conn, actor.id)? { // TODO: maybe the author was added in the meantime diff --git a/plume-models/src/remote_fetch_actor.rs b/plume-models/src/remote_fetch_actor.rs index 097fb1da..9c9808ef 100644 --- a/plume-models/src/remote_fetch_actor.rs +++ b/plume-models/src/remote_fetch_actor.rs @@ -1,6 +1,7 @@ use crate::{ db_conn::{DbConn, DbPool}, follows, + instance::Instance, posts::{LicensedArticle, Post}, users::{User, UserEvent}, ACTOR_SYS, CONFIG, USER_CHAN, @@ -89,7 +90,13 @@ fn fetch_and_cache_followers(user: &Arc, conn: &DbConn) { match follower_ids { Ok(user_ids) => { for user_id in user_ids { - let follower = User::from_id(conn, &user_id, None, CONFIG.proxy()); + let follower = User::from_id( + conn, + &Instance::get_local().expect("Failed to get local instance"), + &user_id, + None, + CONFIG.proxy(), + ); match follower { Ok(follower) => { let inserted = follows::Follow::insert( diff --git a/plume-models/src/reshares.rs b/plume-models/src/reshares.rs index 19bceddf..2d9e7c4e 100644 --- a/plume-models/src/reshares.rs +++ b/plume-models/src/reshares.rs @@ -1,6 +1,6 @@ use crate::{ - db_conn::DbConn, notifications::*, posts::Post, schema::reshares, timeline::*, users::User, - Connection, Error, Result, CONFIG, + db_conn::DbConn, instance::Instance, notifications::*, posts::Post, schema::reshares, + timeline::*, users::User, Connection, Error, Result, CONFIG, }; use activitypub::activity::{Announce, Undo}; use chrono::NaiveDateTime; @@ -142,6 +142,7 @@ impl FromId for Reshare { NewReshare { post_id: Post::from_id( conn, + &Instance::get_local().expect("Failed to get local instance"), &act.announce_props.object_link::()?, None, CONFIG.proxy(), @@ -150,6 +151,7 @@ impl FromId for Reshare { .id, user_id: User::from_id( conn, + &Instance::get_local().expect("Failed to get local instance"), &act.announce_props.actor_link::()?, None, CONFIG.proxy(), diff --git a/plume-models/src/users.rs b/plume-models/src/users.rs index 6b429fd2..0efcd955 100644 --- a/plume-models/src/users.rs +++ b/plume-models/src/users.rs @@ -24,7 +24,7 @@ use plume_common::{ activity_pub::{ ap_accept_header, inbox::{AsActor, AsObject, FromId}, - sign::{gen_keypair, Signer}, + sign::{gen_keypair, Error as SignatureError, Result as SignatureResult, Signer}, ActivityStream, ApSignature, Id, IntoId, PublicKey, PUBLIC_VISIBILITY, }, utils, @@ -210,7 +210,14 @@ impl User { .into_iter() .find(|l| l.mime_type == Some(String::from("application/activity+json"))) .ok_or(Error::Webfinger)?; - User::from_id(conn, link.href.as_ref()?, None, CONFIG.proxy()).map_err(|(_, e)| e) + User::from_id( + conn, + &Instance::get_local().expect("Failed to get local instance"), + link.href.as_ref()?, + None, + CONFIG.proxy(), + ) + .map_err(|(_, e)| e) } pub fn fetch_remote_interact_uri(acct: &str) -> Result { @@ -1065,24 +1072,22 @@ impl AsObject for User { } impl Signer for User { - type Error = Error; - fn get_key_id(&self) -> String { format!("{}#main-key", self.ap_url) } - fn sign(&self, to_sign: &str) -> Result> { + fn sign(&self, to_sign: &str) -> SignatureResult> { let key = self.get_keypair()?; let mut signer = sign::Signer::new(MessageDigest::sha256(), &key)?; signer.update(to_sign.as_bytes())?; - signer.sign_to_vec().map_err(Error::from) + signer.sign_to_vec().map_err(SignatureError::from) } - fn verify(&self, data: &str, signature: &[u8]) -> Result { + fn verify(&self, data: &str, signature: &[u8]) -> SignatureResult { let key = PKey::from_rsa(Rsa::public_key_from_pem(self.public_key.as_ref())?)?; let mut verifier = sign::Verifier::new(MessageDigest::sha256(), &key)?; verifier.update(data.as_bytes())?; - verifier.verify(&signature).map_err(Error::from) + verifier.verify(&signature).map_err(SignatureError::from) } } diff --git a/src/inbox.rs b/src/inbox.rs index a7a67276..ad317242 100644 --- a/src/inbox.rs +++ b/src/inbox.rs @@ -26,8 +26,14 @@ pub fn handle_incoming( .or_else(|| activity["actor"]["id"].as_str()) .ok_or(status::BadRequest(Some("Missing actor id for activity")))?; - let actor = User::from_id(&conn, actor_id, None, CONFIG.proxy()) - .expect("instance::shared_inbox: user error"); + let actor = User::from_id( + &conn, + &Instance::get_local().expect("Failed to get local instance"), + actor_id, + None, + CONFIG.proxy(), + ) + .expect("instance::shared_inbox: user error"); if !verify_http_headers(&actor, &headers.0, &sig).is_secure() && !act.clone().verify(&actor) { // maybe we just know an old key? actor diff --git a/src/routes/instance.rs b/src/routes/instance.rs index aa95bc81..194e9a4a 100644 --- a/src/routes/instance.rs +++ b/src/routes/instance.rs @@ -434,7 +434,13 @@ pub fn interact(conn: DbConn, user: Option, target: String) -> Option, target: String) -> Option