Add verify methods back in, some more fixes (#28)

This commit is contained in:
Nutomic 2023-03-09 22:09:44 +01:00 committed by GitHub
parent 8f2b9634b6
commit 6b3a4f8942
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 164 additions and 18 deletions

2
Cargo.lock generated
View file

@ -4,7 +4,7 @@ version = 3
[[package]]
name = "activitypub_federation"
version = "0.3.4"
version = "0.4.0-rc1"
dependencies = [
"activitystreams-kinds",
"actix-rt",

View file

@ -1,6 +1,6 @@
[package]
name = "activitypub_federation"
version = "0.4.0-rc1"
version = "0.4.0-rc2"
edition = "2021"
description = "High-level Activitypub framework"
keywords = ["activitypub", "activitystreams", "federation", "fediverse"]

View file

@ -37,6 +37,10 @@ impl ActivityHandler for Follow {
self.actor.inner()
}
async fn verify(&self, _data: &RequestData<Self::DataType>) -> Result<(), Self::Error> {
Ok(())
}
async fn receive(self, data: &RequestData<Self::DataType>) -> Result<(), Self::Error> {
let actor = self.actor.dereference(data).await?;
let followed = self.object.dereference(data).await?;

View file

@ -45,6 +45,10 @@ impl ApubObject for SearchableDbObjects {
unimplemented!();
}
async fn verify(apub: &Self::ApubType, expected_domain: &Url, _data: &RequestData<Self::DataType>) -> Result<(), Self::Error> {
Ok(())
}
async fn from_apub(
apub: Self::ApubType,
data: &RequestData<Self::DataType>,

View file

@ -65,6 +65,11 @@ impl ActivityHandler for CreatePost {
self.actor.inner()
}
async fn verify(&self, data: &RequestData<Self::DataType>) -> Result<(), Self::Error> {
DbPost::verify(&self.object, &self.id, data).await?;
Ok(())
}
async fn receive(self, data: &RequestData<Self::DataType>) -> Result<(), Self::Error> {
DbPost::from_apub(self.object, data).await?;
Ok(())

View file

@ -4,7 +4,7 @@ use activitypub_federation::{
fetch::object_id::ObjectId,
http_signatures::generate_actor_keypair,
kinds::actor::PersonType,
protocol::public_key::PublicKey,
protocol::{public_key::PublicKey, verification::verify_domains_match},
traits::{ActivityHandler, Actor, ApubObject},
};
use chrono::{Local, NaiveDateTime};
@ -99,6 +99,15 @@ impl ApubObject for DbUser {
})
}
async fn verify(
apub: &Self::ApubType,
expected_domain: &Url,
_data: &RequestData<Self::DataType>,
) -> Result<(), Self::Error> {
verify_domains_match(apub.id.inner(), expected_domain)?;
Ok(())
}
async fn from_apub(
apub: Self::ApubType,
_data: &RequestData<Self::DataType>,
@ -117,11 +126,15 @@ impl ApubObject for DbUser {
}
impl Actor for DbUser {
fn public_key(&self) -> &str {
fn public_key_pem(&self) -> &str {
&self.public_key
}
fn inbox(&self) -> Url {
self.inbox.clone()
}
fn id(&self) -> &Url {
self.ap_id.inner()
}
}

View file

@ -9,7 +9,7 @@ use activitypub_federation::{
config::RequestData,
fetch::object_id::ObjectId,
kinds::{object::NoteType, public},
protocol::helpers::deserialize_one_or_many,
protocol::{helpers::deserialize_one_or_many, verification::verify_domains_match},
traits::{Actor, ApubObject},
};
use activitystreams_kinds::link::MentionType;
@ -65,6 +65,15 @@ impl ApubObject for DbPost {
unimplemented!()
}
async fn verify(
apub: &Self::ApubType,
expected_domain: &Url,
_data: &RequestData<Self::DataType>,
) -> Result<(), Self::Error> {
verify_domains_match(apub.id.inner(), expected_domain)?;
Ok(())
}
async fn from_apub(
apub: Self::ApubType,
data: &RequestData<Self::DataType>,

View file

@ -42,6 +42,10 @@ impl ActivityHandler for Accept {
self.actor.inner()
}
async fn verify(&self, _data: &RequestData<Self::DataType>) -> Result<(), Self::Error> {
Ok(())
}
async fn receive(self, _data: &RequestData<Self::DataType>) -> Result<(), Self::Error> {
Ok(())
}

View file

@ -50,6 +50,11 @@ impl ActivityHandler for CreatePost {
self.actor.inner()
}
async fn verify(&self, data: &RequestData<Self::DataType>) -> Result<(), Self::Error> {
DbPost::verify(&self.object, &self.id, data).await?;
Ok(())
}
async fn receive(self, data: &RequestData<Self::DataType>) -> Result<(), Self::Error> {
DbPost::from_apub(self.object, data).await?;
Ok(())

View file

@ -47,6 +47,10 @@ impl ActivityHandler for Follow {
self.actor.inner()
}
async fn verify(&self, _data: &RequestData<Self::DataType>) -> Result<(), Self::Error> {
Ok(())
}
// Ignore clippy false positive: https://github.com/rust-lang/rust-clippy/issues/6446
#[allow(clippy::await_holding_lock)]
async fn receive(self, data: &RequestData<Self::DataType>) -> Result<(), Self::Error> {

View file

@ -11,7 +11,7 @@ use activitypub_federation::{
fetch::{object_id::ObjectId, webfinger::webfinger_resolve_actor},
http_signatures::generate_actor_keypair,
kinds::actor::PersonType,
protocol::{context::WithContext, public_key::PublicKey},
protocol::{context::WithContext, public_key::PublicKey, verification::verify_domains_match},
traits::{ActivityHandler, Actor, ApubObject},
};
use chrono::{Local, NaiveDateTime};
@ -171,6 +171,15 @@ impl ApubObject for DbUser {
})
}
async fn verify(
apub: &Self::ApubType,
expected_domain: &Url,
_data: &RequestData<Self::DataType>,
) -> Result<(), Self::Error> {
verify_domains_match(apub.id.inner(), expected_domain)?;
Ok(())
}
async fn from_apub(
apub: Self::ApubType,
data: &RequestData<Self::DataType>,
@ -192,7 +201,11 @@ impl ApubObject for DbUser {
}
impl Actor for DbUser {
fn public_key(&self) -> &str {
fn id(&self) -> &Url {
self.ap_id.inner()
}
fn public_key_pem(&self) -> &str {
&self.public_key
}

View file

@ -3,7 +3,7 @@ use activitypub_federation::{
config::RequestData,
fetch::object_id::ObjectId,
kinds::{object::NoteType, public},
protocol::helpers::deserialize_one_or_many,
protocol::{helpers::deserialize_one_or_many, verification::verify_domains_match},
traits::ApubObject,
};
use serde::{Deserialize, Serialize};
@ -73,6 +73,15 @@ impl ApubObject for DbPost {
})
}
async fn verify(
apub: &Self::ApubType,
expected_domain: &Url,
_data: &RequestData<Self::DataType>,
) -> Result<(), Self::Error> {
verify_domains_match(apub.id.inner(), expected_domain)?;
Ok(())
}
async fn from_apub(
apub: Self::ApubType,
data: &RequestData<Self::DataType>,

View file

@ -42,10 +42,11 @@ where
request.headers(),
request.method(),
request.uri(),
actor.public_key(),
actor.public_key_pem(),
)?;
debug!("Receiving activity {}", activity.id().to_string());
activity.verify(data).await?;
activity.receive(data).await?;
Ok(HttpResponse::Ok().finish())
}

View file

@ -44,15 +44,15 @@ where
.dereference(data)
.await?;
// TODO: why do errors here not get returned over http?
verify_signature(
&activity_data.headers,
&activity_data.method,
&activity_data.uri,
actor.public_key(),
actor.public_key_pem(),
)?;
debug!("Receiving activity {}", activity.id().to_string());
activity.verify(data).await?;
activity.receive(data).await?;
Ok(())
}

View file

@ -148,6 +148,7 @@ where
let res2 = res?;
Kind::verify(&res2, self.inner(), data).await?;
Kind::from_apub(res2, data).await
}
}

View file

@ -62,7 +62,7 @@ impl<T> WithContext<T> {
#[async_trait::async_trait]
impl<T> ActivityHandler for WithContext<T>
where
T: ActivityHandler + Send,
T: ActivityHandler + Send + Sync,
{
type DataType = <T as ActivityHandler>::DataType;
type Error = <T as ActivityHandler>::Error;
@ -75,6 +75,10 @@ where
self.inner.actor()
}
async fn verify(&self, data: &RequestData<Self::DataType>) -> Result<(), Self::Error> {
self.inner.verify(data).await
}
async fn receive(self, data: &RequestData<Self::DataType>) -> Result<(), Self::Error> {
self.inner.receive(data).await
}

View file

@ -1,6 +1,6 @@
//! Traits which need to be implemented for federated data types
use crate::config::RequestData;
use crate::{config::RequestData, protocol::public_key::PublicKey};
use async_trait::async_trait;
use chrono::NaiveDateTime;
use std::ops::Deref;
@ -13,6 +13,7 @@ use url::Url;
/// # use url::Url;
/// # use activitypub_federation::protocol::public_key::PublicKey;
/// # use activitypub_federation::config::RequestData;
/// use activitypub_federation::protocol::verification::verify_domains_match;
/// # use activitypub_federation::traits::ApubObject;
/// # use activitypub_federation::traits::tests::{DbConnection, Person};
/// # pub struct DbUser {
@ -53,7 +54,13 @@ use url::Url;
/// })
/// }
///
/// async fn from_apub(apub: Self::ApubType, data: &RequestData<Self::DataType>) -> Result<Self, Self::Error> {
/// async fn verify(apub: &Self::ApubType, expected_domain: &Url, data: &RequestData<Self::DataType>,) -> Result<(), Self::Error> {
/// verify_domains_match(apub.id.inner(), expected_domain)?;
/// // additional application specific checks
/// Ok(())
/// }
///
/// async fn from_apub(apub: Self::ApubType, data: &RequestData<Self::DataType>) -> Result<Self, Self::Error> {
/// // Called when a remote object gets received over Activitypub. Validate and insert it
/// // into the database.
///
@ -124,6 +131,19 @@ pub trait ApubObject: Sized {
data: &RequestData<Self::DataType>,
) -> Result<Self::ApubType, Self::Error>;
/// Verifies that the received object is valid.
///
/// You should check here that the domain of id matches `expected_domain`. Additionally you
/// should perform any application specific checks.
///
/// It is necessary to use a separate method for this, because it might be used for activities
/// like `Delete/Note`, which shouldn't perform any database write for the inner `Note`.
async fn verify(
apub: &Self::ApubType,
expected_domain: &Url,
data: &RequestData<Self::DataType>,
) -> Result<(), Self::Error>;
/// Convert object from ActivityPub type to database type.
///
/// Called when an object is received from HTTP fetch or as part of an activity. This method
@ -166,6 +186,10 @@ pub trait ApubObject: Sized {
/// self.actor.inner()
/// }
///
/// async fn verify(&self, data: &RequestData<Self::DataType>) -> Result<(), Self::Error> {
/// Ok(())
/// }
///
/// async fn receive(self, data: &RequestData<Self::DataType>) -> Result<(), Self::Error> {
/// let local_user = self.object.dereference(data).await?;
/// let follower = self.actor.dereference(data).await?;
@ -189,6 +213,12 @@ pub trait ActivityHandler {
/// `actor` field of activity
fn actor(&self) -> &Url;
/// Verifies that the received activity is valid.
///
/// This needs to be a separate method, because it might be used for activities
/// like `Undo/Follow`, which shouldn't perform any database write for the inner `Follow`.
async fn verify(&self, data: &RequestData<Self::DataType>) -> Result<(), Self::Error>;
/// Called when an activity is received.
///
/// Should perform validation and possibly write action to the database. In case the activity
@ -198,12 +228,23 @@ pub trait ActivityHandler {
/// Trait to allow retrieving common Actor data.
pub trait Actor: ApubObject {
/// `id` field of the actor
fn id(&self) -> &Url;
/// The actor's public key for verifying signatures of incoming activities.
fn public_key(&self) -> &str;
///
/// Use [generate_actor_keypair](crate::http_signatures::generate_actor_keypair) to create the
/// actor keypair.
fn public_key_pem(&self) -> &str;
/// The inbox where activities for this user should be sent to
fn inbox(&self) -> Url;
/// Generates a public key struct for use in the actor json representation
fn public_key(&self) -> PublicKey {
PublicKey::new(self.id().clone(), self.public_key_pem().to_string())
}
/// The actor's shared inbox, if any
fn shared_inbox(&self) -> Option<Url> {
None
@ -219,7 +260,7 @@ pub trait Actor: ApubObject {
#[async_trait]
impl<T> ActivityHandler for Box<T>
where
T: ActivityHandler + Send,
T: ActivityHandler + Send + Sync,
{
type DataType = T::DataType;
type Error = T::Error;
@ -232,6 +273,10 @@ where
self.deref().actor()
}
async fn verify(&self, data: &RequestData<Self::DataType>) -> Result<(), Self::Error> {
(*self).verify(data).await
}
async fn receive(self, data: &RequestData<Self::DataType>) -> Result<(), Self::Error> {
(*self).receive(data).await
}
@ -247,7 +292,7 @@ pub mod tests {
use crate::{
fetch::object_id::ObjectId,
http_signatures::{generate_actor_keypair, Keypair},
protocol::public_key::PublicKey,
protocol::{public_key::PublicKey, verification::verify_domains_match},
};
use activitystreams_kinds::{activity::FollowType, actor::PersonType};
use anyhow::Error;
@ -333,6 +378,15 @@ pub mod tests {
})
}
async fn verify(
apub: &Self::ApubType,
expected_domain: &Url,
_data: &RequestData<Self::DataType>,
) -> Result<(), Self::Error> {
verify_domains_match(apub.id.inner(), expected_domain)?;
Ok(())
}
async fn from_apub(
apub: Self::ApubType,
_data: &RequestData<Self::DataType>,
@ -350,7 +404,11 @@ pub mod tests {
}
impl Actor for DbUser {
fn public_key(&self) -> &str {
fn id(&self) -> &Url {
&self.apub_id
}
fn public_key_pem(&self) -> &str {
&self.public_key
}
@ -382,6 +440,10 @@ pub mod tests {
self.actor.inner()
}
async fn verify(&self, _: &RequestData<Self::DataType>) -> Result<(), Self::Error> {
Ok(())
}
async fn receive(self, _data: &RequestData<Self::DataType>) -> Result<(), Self::Error> {
Ok(())
}
@ -413,6 +475,14 @@ pub mod tests {
todo!()
}
async fn verify(
_: &Self::ApubType,
_: &Url,
_: &RequestData<Self::DataType>,
) -> Result<(), Self::Error> {
todo!()
}
async fn from_apub(
_: Self::ApubType,
_: &RequestData<Self::DataType>,