Sign GET request to external instances
This commit is contained in:
+32
-10
@@ -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<CustomGroup> {
|
||||
@@ -374,7 +384,14 @@ impl FromId<DbConn> 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<DbConn> 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<Vec<u8>> {
|
||||
fn sign(&self, to_sign: &str) -> SignatureResult<Vec<u8>> {
|
||||
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<bool> {
|
||||
fn verify(&self, data: &str, signature: &[u8]) -> SignatureResult<bool> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -236,6 +236,7 @@ impl FromId<DbConn> for Comment {
|
||||
})?,
|
||||
author_id: User::from_id(
|
||||
conn,
|
||||
&Instance::get_local().expect("Failed to get local instance"),
|
||||
¬e.object_props.attributed_to_link::<Id>()?,
|
||||
None,
|
||||
CONFIG.proxy(),
|
||||
@@ -294,7 +295,13 @@ impl FromId<DbConn> for Comment {
|
||||
.collect::<HashSet<_>>() // 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
|
||||
|
||||
@@ -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<DbConn> for Follow {
|
||||
fn from_activity(conn: &DbConn, follow: FollowAct) -> Result<Self> {
|
||||
let actor = User::from_id(
|
||||
conn,
|
||||
&Instance::get_local().expect("Failed to get local instance"),
|
||||
&follow.follow_props.actor_link::<Id>()?,
|
||||
None,
|
||||
CONFIG.proxy(),
|
||||
@@ -176,6 +177,7 @@ impl FromId<DbConn> for Follow {
|
||||
|
||||
let target = User::from_id(
|
||||
conn,
|
||||
&Instance::get_local().expect("Failed to get local instance"),
|
||||
&follow.follow_props.object_link::<Id>()?,
|
||||
None,
|
||||
CONFIG.proxy(),
|
||||
|
||||
+21
-15
@@ -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<InboxResult, Error> {
|
||||
Inbox::handle(conn, act)
|
||||
.with::<User, Announce, Post>(CONFIG.proxy())
|
||||
.with::<User, Create, Comment>(CONFIG.proxy())
|
||||
.with::<User, Create, Post>(CONFIG.proxy())
|
||||
.with::<User, Delete, Comment>(CONFIG.proxy())
|
||||
.with::<User, Delete, Post>(CONFIG.proxy())
|
||||
.with::<User, Delete, User>(CONFIG.proxy())
|
||||
.with::<User, Follow, User>(CONFIG.proxy())
|
||||
.with::<User, Like, Post>(CONFIG.proxy())
|
||||
.with::<User, Undo, Reshare>(CONFIG.proxy())
|
||||
.with::<User, Undo, follows::Follow>(CONFIG.proxy())
|
||||
.with::<User, Undo, likes::Like>(CONFIG.proxy())
|
||||
.with::<User, Update, PostUpdate>(CONFIG.proxy())
|
||||
.done()
|
||||
Inbox::handle(
|
||||
conn,
|
||||
&Instance::get_local().expect("Failed to get local instance"),
|
||||
act,
|
||||
)
|
||||
.with::<User, Announce, Post>(CONFIG.proxy())
|
||||
.with::<User, Create, Comment>(CONFIG.proxy())
|
||||
.with::<User, Create, Post>(CONFIG.proxy())
|
||||
.with::<User, Delete, Comment>(CONFIG.proxy())
|
||||
.with::<User, Delete, Post>(CONFIG.proxy())
|
||||
.with::<User, Delete, User>(CONFIG.proxy())
|
||||
.with::<User, Follow, User>(CONFIG.proxy())
|
||||
.with::<User, Like, Post>(CONFIG.proxy())
|
||||
.with::<User, Undo, Reshare>(CONFIG.proxy())
|
||||
.with::<User, Undo, follows::Follow>(CONFIG.proxy())
|
||||
.with::<User, Undo, likes::Like>(CONFIG.proxy())
|
||||
.with::<User, Update, PostUpdate>(CONFIG.proxy())
|
||||
.done()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -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<Vec<u8>> {
|
||||
fn sign(&self, to_sign: &str) -> SignatureResult<Vec<u8>> {
|
||||
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<bool> {
|
||||
fn verify(&self, data: &str, signature: &[u8]) -> SignatureResult<bool> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
-1
@@ -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<openssl::error::ErrorStack> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sign::Error> for Error {
|
||||
fn from(_: sign::Error) -> Self {
|
||||
Error::Signature
|
||||
}
|
||||
}
|
||||
|
||||
impl From<diesel::result::Error> for Error {
|
||||
fn from(err: diesel::result::Error) -> Self {
|
||||
Error::Db(err)
|
||||
@@ -162,6 +168,12 @@ impl From<InboxError<Error>> for Error {
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
impl From<Error> 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.
|
||||
///
|
||||
|
||||
@@ -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<DbConn> for Like {
|
||||
NewLike {
|
||||
post_id: Post::from_id(
|
||||
conn,
|
||||
&Instance::get_local().expect("Failed to get local instance"),
|
||||
&act.like_props.object_link::<Id>()?,
|
||||
None,
|
||||
CONFIG.proxy(),
|
||||
@@ -125,6 +126,7 @@ impl FromId<DbConn> for Like {
|
||||
.id,
|
||||
user_id: User::from_id(
|
||||
conn,
|
||||
&Instance::get_local().expect("Failed to get local instance"),
|
||||
&act.like_props.actor_link::<Id>()?,
|
||||
None,
|
||||
CONFIG.proxy(),
|
||||
|
||||
@@ -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::<Id>()
|
||||
|
||||
@@ -630,13 +630,28 @@ impl FromId<DbConn> 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<User, Update, &DbConn> 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
|
||||
|
||||
@@ -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<User>, 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(
|
||||
|
||||
@@ -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<DbConn> for Reshare {
|
||||
NewReshare {
|
||||
post_id: Post::from_id(
|
||||
conn,
|
||||
&Instance::get_local().expect("Failed to get local instance"),
|
||||
&act.announce_props.object_link::<Id>()?,
|
||||
None,
|
||||
CONFIG.proxy(),
|
||||
@@ -150,6 +151,7 @@ impl FromId<DbConn> for Reshare {
|
||||
.id,
|
||||
user_id: User::from_id(
|
||||
conn,
|
||||
&Instance::get_local().expect("Failed to get local instance"),
|
||||
&act.announce_props.actor_link::<Id>()?,
|
||||
None,
|
||||
CONFIG.proxy(),
|
||||
|
||||
@@ -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<String> {
|
||||
@@ -1065,24 +1072,22 @@ impl AsObject<User, Delete, &DbConn> 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<Vec<u8>> {
|
||||
fn sign(&self, to_sign: &str) -> SignatureResult<Vec<u8>> {
|
||||
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<bool> {
|
||||
fn verify(&self, data: &str, signature: &[u8]) -> SignatureResult<bool> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user