convert inbox functions, add missing checks

This commit is contained in:
Felix Ableitner 2021-06-27 16:54:24 +02:00
parent 074febb960
commit 5460c658a7
14 changed files with 165 additions and 134 deletions

View file

@ -38,6 +38,7 @@ async fn get_notif_recipients(
// Although mentions could be gotten from the post tags (they are included there), or the ccs, // Although mentions could be gotten from the post tags (they are included there), or the ccs,
// Its much easier to scrape them from the comment body, since the API has to do that // Its much easier to scrape them from the comment body, since the API has to do that
// anyway. // anyway.
// TODO: for compatibility with other projects, it would be much better to read this from cc or tags
let mentions = scrape_text_for_mentions(&comment.content); let mentions = scrape_text_for_mentions(&comment.content);
send_local_notifs(mentions, comment.clone(), actor, post, context.pool(), true).await send_local_notifs(mentions, comment.clone(), actor, post, context.pool(), true).await
} }

View file

@ -34,6 +34,7 @@ pub struct DeleteCommunity {
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl VerifyActivity for Activity<DeleteCommunity> { impl VerifyActivity for Activity<DeleteCommunity> {
async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> { async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> {
verify_domains_match(&self.inner.actor, self.id_unchecked())?;
let object = self.inner.object.clone(); let object = self.inner.object.clone();
let community = blocking(context.pool(), move |conn| { let community = blocking(context.pool(), move |conn| {
Community::read_from_apub_id(conn, &object.into()) Community::read_from_apub_id(conn, &object.into())

View file

@ -26,6 +26,7 @@ pub struct RemoveCommunity {
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl VerifyActivity for Activity<RemoveCommunity> { impl VerifyActivity for Activity<RemoveCommunity> {
async fn verify(&self, _context: &LemmyContext) -> Result<(), LemmyError> { async fn verify(&self, _context: &LemmyContext) -> Result<(), LemmyError> {
verify_domains_match(&self.inner.actor, self.id_unchecked())?;
check_is_apub_id_valid(&self.inner.actor, false)?; check_is_apub_id_valid(&self.inner.actor, false)?;
verify_domains_match(&self.inner.actor, &self.inner.object)?; verify_domains_match(&self.inner.actor, &self.inner.object)?;
verify_domains_match(&self.inner.actor, &self.inner.cc[0]) verify_domains_match(&self.inner.actor, &self.inner.cc[0])

View file

@ -38,6 +38,7 @@ pub struct UndoDeleteCommunity {
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl VerifyActivity for Activity<UndoDeleteCommunity> { impl VerifyActivity for Activity<UndoDeleteCommunity> {
async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> { async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> {
verify_domains_match(&self.inner.actor, self.id_unchecked())?;
let object = self.inner.object.inner.object.clone(); let object = self.inner.object.inner.object.clone();
let community = blocking(context.pool(), move |conn| { let community = blocking(context.pool(), move |conn| {
Community::read_from_apub_id(conn, &object.into()) Community::read_from_apub_id(conn, &object.into())

View file

@ -26,6 +26,7 @@ pub struct UndoRemoveCommunity {
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl VerifyActivity for Activity<UndoRemoveCommunity> { impl VerifyActivity for Activity<UndoRemoveCommunity> {
async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> { async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> {
verify_domains_match(&self.inner.actor, self.id_unchecked())?;
check_is_apub_id_valid(&self.inner.actor, false)?; check_is_apub_id_valid(&self.inner.actor, false)?;
verify_domains_match(&self.inner.actor, &self.inner.object.inner.object)?; verify_domains_match(&self.inner.actor, &self.inner.object.inner.object)?;
verify_domains_match(&self.inner.actor, &self.inner.cc[0])?; verify_domains_match(&self.inner.actor, &self.inner.cc[0])?;

View file

@ -5,7 +5,7 @@ use crate::{
use activitystreams::{activity::kind::UpdateType, base::BaseExt}; use activitystreams::{activity::kind::UpdateType, base::BaseExt};
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub::{check_is_apub_id_valid, objects::FromApubToForm, GroupExt}; use lemmy_apub::{check_is_apub_id_valid, objects::FromApubToForm, GroupExt};
use lemmy_apub_lib::{PublicUrl, ReceiveActivity, VerifyActivity}; use lemmy_apub_lib::{verify_domains_match, PublicUrl, ReceiveActivity, VerifyActivity};
use lemmy_db_queries::{ApubObject, Crud}; use lemmy_db_queries::{ApubObject, Crud};
use lemmy_db_schema::source::community::{Community, CommunityForm}; use lemmy_db_schema::source::community::{Community, CommunityForm};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
@ -28,6 +28,7 @@ pub struct UpdateCommunity {
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl VerifyActivity for Activity<UpdateCommunity> { impl VerifyActivity for Activity<UpdateCommunity> {
async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> { async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> {
verify_domains_match(&self.inner.actor, self.id_unchecked())?;
self.inner.object.id(self.inner.cc[0].as_str())?; self.inner.object.id(self.inner.cc[0].as_str())?;
check_is_apub_id_valid(&self.inner.actor, false)?; check_is_apub_id_valid(&self.inner.actor, false)?;
verify_is_community_mod(self.inner.actor.clone(), self.inner.cc[0].clone(), context).await verify_is_community_mod(self.inner.actor.clone(), self.inner.cc[0].clone(), context).await

View file

@ -1,9 +1,9 @@
use crate::inbox::new_inbox_routing::Activity; use crate::{activities_new::follow::follow::FollowCommunity, inbox::new_inbox_routing::Activity};
use activitystreams::activity::kind::{AcceptType, FollowType}; use activitystreams::activity::kind::AcceptType;
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub::fetcher::{ use lemmy_apub::{
community::get_or_fetch_and_upsert_community, check_is_apub_id_valid,
person::get_or_fetch_and_upsert_person, fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
}; };
use lemmy_apub_lib::{verify_domains_match, ReceiveActivity, VerifyActivity}; use lemmy_apub_lib::{verify_domains_match, ReceiveActivity, VerifyActivity};
use lemmy_db_queries::Followable; use lemmy_db_queries::Followable;
@ -12,35 +12,6 @@ use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use url::Url; use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FollowCommunity {
actor: Url,
to: Url,
object: Url,
#[serde(rename = "type")]
kind: FollowType,
}
#[async_trait::async_trait(?Send)]
impl VerifyActivity for Activity<FollowCommunity> {
async fn verify(&self, _context: &LemmyContext) -> Result<(), LemmyError> {
todo!()
}
}
#[async_trait::async_trait(?Send)]
impl ReceiveActivity for Activity<FollowCommunity> {
async fn receive(
&self,
_context: &LemmyContext,
_request_counter: &mut i32,
) -> Result<(), LemmyError> {
println!("receive follow");
todo!()
}
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct AcceptFollowCommunity { pub struct AcceptFollowCommunity {
@ -54,7 +25,8 @@ pub struct AcceptFollowCommunity {
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl VerifyActivity for Activity<AcceptFollowCommunity> { impl VerifyActivity for Activity<AcceptFollowCommunity> {
async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> { async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> {
verify_domains_match(self.id_unchecked(), &self.inner.actor)?; verify_domains_match(&self.inner.actor, self.id_unchecked())?;
check_is_apub_id_valid(&self.inner.actor, false)?;
self.inner.object.verify(context).await self.inner.object.verify(context).await
} }
} }

View file

@ -0,0 +1,67 @@
use crate::inbox::new_inbox_routing::Activity;
use activitystreams::{
activity::{kind::FollowType, Follow},
base::{AnyBase, ExtendsExt},
};
use anyhow::Context;
use lemmy_api_common::blocking;
use lemmy_apub::{
check_is_apub_id_valid,
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
CommunityType,
};
use lemmy_apub_lib::{verify_domains_match, ReceiveActivity, VerifyActivity};
use lemmy_db_queries::Followable;
use lemmy_db_schema::source::community::{CommunityFollower, CommunityFollowerForm};
use lemmy_utils::{location_info, LemmyError};
use lemmy_websocket::LemmyContext;
use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FollowCommunity {
actor: Url,
to: Url,
pub(in crate::activities_new::follow) object: Url,
#[serde(rename = "type")]
kind: FollowType,
}
#[async_trait::async_trait(?Send)]
impl VerifyActivity for Activity<FollowCommunity> {
async fn verify(&self, _context: &LemmyContext) -> Result<(), LemmyError> {
verify_domains_match(&self.inner.actor, self.id_unchecked())?;
verify_domains_match(&self.inner.to, &self.inner.object)?;
check_is_apub_id_valid(&self.inner.actor, false)
}
}
#[async_trait::async_trait(?Send)]
impl ReceiveActivity for Activity<FollowCommunity> {
async fn receive(
&self,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let community =
get_or_fetch_and_upsert_community(&self.inner.object, context, request_counter).await?;
let person =
get_or_fetch_and_upsert_person(&self.inner.actor, context, request_counter).await?;
let community_follower_form = CommunityFollowerForm {
community_id: community.id,
person_id: person.id,
pending: false,
};
// This will fail if they're already a follower, but ignore the error.
blocking(&context.pool(), move |conn| {
CommunityFollower::follow(&conn, &community_follower_form).ok()
})
.await?;
// TODO: avoid the conversion and pass our own follow struct directly
let anybase = AnyBase::from_arbitrary_json(serde_json::to_string(self)?)?;
let anybase = Follow::from_any_base(anybase)?.context(location_info!())?;
community.send_accept_follow(anybase, context).await
}
}

View file

@ -0,0 +1,3 @@
pub mod accept;
pub mod follow;
pub mod undo;

View file

@ -0,0 +1,60 @@
use crate::{activities_new::follow::follow::FollowCommunity, inbox::new_inbox_routing::Activity};
use activitystreams::activity::kind::UndoType;
use lemmy_api_common::blocking;
use lemmy_apub::{
check_is_apub_id_valid,
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
};
use lemmy_apub_lib::{verify_domains_match, ReceiveActivity, VerifyActivity};
use lemmy_db_queries::Followable;
use lemmy_db_schema::source::community::{CommunityFollower, CommunityFollowerForm};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UndoFollowCommunity {
actor: Url,
to: Url,
object: Activity<FollowCommunity>,
#[serde(rename = "type")]
kind: UndoType,
}
#[async_trait::async_trait(?Send)]
impl VerifyActivity for Activity<UndoFollowCommunity> {
async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> {
verify_domains_match(&self.inner.actor, self.id_unchecked())?;
verify_domains_match(&self.inner.to, &self.inner.object.inner.object)?;
check_is_apub_id_valid(&self.inner.actor, false)?;
self.inner.object.verify(context).await
}
}
#[async_trait::async_trait(?Send)]
impl ReceiveActivity for Activity<UndoFollowCommunity> {
async fn receive(
&self,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let community =
get_or_fetch_and_upsert_community(&self.inner.to, context, request_counter).await?;
let person =
get_or_fetch_and_upsert_person(&self.inner.actor, context, request_counter).await?;
let community_follower_form = CommunityFollowerForm {
community_id: community.id,
person_id: person.id,
pending: false,
};
// This will fail if they aren't a follower, but ignore the error.
blocking(&context.pool(), move |conn| {
CommunityFollower::unfollow(&conn, &community_follower_form).ok()
})
.await?;
Ok(())
}
}

View file

@ -36,7 +36,6 @@ impl ReceiveActivity for Activity<DeletePost> {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
// TODO: check that actor is from same instance as post (same for DeleteComment)
let post = get_or_fetch_and_insert_post(&self.inner.object, context, request_counter).await?; let post = get_or_fetch_and_insert_post(&self.inner.object, context, request_counter).await?;
let deleted_post = blocking(context.pool(), move |conn| { let deleted_post = blocking(context.pool(), move |conn| {

View file

@ -39,7 +39,7 @@ impl ReceiveActivity for Activity<RemovePost> {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
// TODO: check that actor is instance mod if community is local (same for DeleteComment) // TODO: check that actor is instance mod if community is local (same for RemoveComment)
let post = get_or_fetch_and_insert_post(&self.inner.object, context, request_counter).await?; let post = get_or_fetch_and_insert_post(&self.inner.object, context, request_counter).await?;
let removed_post = blocking(context.pool(), move |conn| { let removed_post = blocking(context.pool(), move |conn| {

View file

@ -1,6 +1,4 @@
use crate::{ use crate::inbox::{
activities::receive::verify_activity_domains_valid,
inbox::{
assert_activity_not_local, assert_activity_not_local,
get_activity_id, get_activity_id,
inbox_verify_http_signature, inbox_verify_http_signature,
@ -12,11 +10,9 @@ use crate::{
receive_undo_for_community, receive_undo_for_community,
}, },
verify_is_addressed_to_public, verify_is_addressed_to_public,
},
}; };
use activitystreams::{ use activitystreams::{
activity::{kind::FollowType, ActorAndObject, Follow, Undo}, activity::{kind::FollowType, ActorAndObject},
base::AnyBase,
prelude::*, prelude::*,
}; };
use actix_web::{web, HttpRequest, HttpResponse}; use actix_web::{web, HttpRequest, HttpResponse};
@ -29,11 +25,8 @@ use lemmy_apub::{
ActorType, ActorType,
CommunityType, CommunityType,
}; };
use lemmy_db_queries::{source::community::Community_, ApubObject, Followable}; use lemmy_db_queries::{source::community::Community_, ApubObject};
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{community::Community, person::Person};
community::{Community, CommunityFollower, CommunityFollowerForm},
person::Person,
};
use lemmy_utils::{location_info, LemmyError}; use lemmy_utils::{location_info, LemmyError};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use log::info; use log::info;
@ -132,16 +125,7 @@ pub(crate) async fn community_receive_message(
let actor_url = actor.actor_id(); let actor_url = actor.actor_id();
let activity_kind = activity.kind().context(location_info!())?; let activity_kind = activity.kind().context(location_info!())?;
let do_announce = match activity_kind { let do_announce = match activity_kind {
CommunityValidTypes::Follow => { CommunityValidTypes::Follow => todo!(),
Box::pin(handle_follow(
any_base.clone(),
person,
&to_community,
&context,
))
.await?;
false
}
CommunityValidTypes::Undo => { CommunityValidTypes::Undo => {
Box::pin(handle_undo( Box::pin(handle_undo(
context, context,
@ -212,39 +196,11 @@ pub(crate) async fn community_receive_message(
Ok(HttpResponse::Ok().finish()) Ok(HttpResponse::Ok().finish())
} }
/// Handle a follow request from a remote person, adding the person as follower and returning an
/// Accept activity.
async fn handle_follow(
activity: AnyBase,
person: Person,
community: &Community,
context: &LemmyContext,
) -> Result<HttpResponse, LemmyError> {
let follow = Follow::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&follow, &person.actor_id(), false)?;
let community_follower_form = CommunityFollowerForm {
community_id: community.id,
person_id: person.id,
pending: false,
};
// This will fail if they're already a follower, but ignore the error.
blocking(&context.pool(), move |conn| {
CommunityFollower::follow(&conn, &community_follower_form).ok()
})
.await?;
community.send_accept_follow(follow, context).await?;
Ok(HttpResponse::Ok().finish())
}
async fn handle_undo( async fn handle_undo(
context: &LemmyContext, context: &LemmyContext,
activity: CommunityAcceptedActivities, activity: CommunityAcceptedActivities,
actor_url: Url, actor_url: Url,
to_community: &Community, _to_community: &Community,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<bool, LemmyError> { ) -> Result<bool, LemmyError> {
let inner_kind = activity let inner_kind = activity
@ -252,43 +208,9 @@ async fn handle_undo(
.is_single_kind(&FollowType::Follow.to_string()); .is_single_kind(&FollowType::Follow.to_string());
let any_base = activity.into_any_base()?; let any_base = activity.into_any_base()?;
if inner_kind { if inner_kind {
handle_undo_follow(any_base, actor_url, to_community, &context).await?; todo!()
Ok(false)
} else { } else {
receive_undo_for_community(context, any_base, None, &actor_url, request_counter).await?; receive_undo_for_community(context, any_base, None, &actor_url, request_counter).await?;
Ok(true) Ok(true)
} }
} }
/// Handle `Undo/Follow` from a person, removing the person from followers list.
async fn handle_undo_follow(
activity: AnyBase,
person_url: Url,
community: &Community,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let undo = Undo::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&undo, &person_url, true)?;
let object = undo.object().to_owned().one().context(location_info!())?;
let follow = Follow::from_any_base(object)?.context(location_info!())?;
verify_activity_domains_valid(&follow, &person_url, false)?;
let person = blocking(&context.pool(), move |conn| {
Person::read_from_apub_id(&conn, &person_url.into())
})
.await??;
let community_follower_form = CommunityFollowerForm {
community_id: community.id,
person_id: person.id,
pending: false,
};
// This will fail if they aren't a follower, but ignore the error.
blocking(&context.pool(), move |conn| {
CommunityFollower::unfollow(&conn, &community_follower_form).ok()
})
.await?;
Ok(())
}

View file

@ -14,7 +14,7 @@ use crate::activities_new::{
undo_remove::UndoRemoveCommunity, undo_remove::UndoRemoveCommunity,
update::UpdateCommunity, update::UpdateCommunity,
}, },
follow::AcceptFollowCommunity, follow::{accept::AcceptFollowCommunity, follow::FollowCommunity, undo::UndoFollowCommunity},
post::{ post::{
create::CreatePost, create::CreatePost,
delete::DeletePost, delete::DeletePost,
@ -63,7 +63,9 @@ impl<Kind> Activity<Kind> {
// TODO: this is probably wrong, it contains all activities // TODO: this is probably wrong, it contains all activities
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub enum PersonAcceptedActivitiesNew { pub enum PersonAcceptedActivitiesNew {
FollowCommunity(FollowCommunity),
AcceptFollowCommunity(AcceptFollowCommunity), AcceptFollowCommunity(AcceptFollowCommunity),
UndoFollowCommunity(UndoFollowCommunity),
CreatePrivateMessage(CreatePrivateMessage), CreatePrivateMessage(CreatePrivateMessage),
UpdatePrivateMessage(UpdatePrivateMessage), UpdatePrivateMessage(UpdatePrivateMessage),
DeletePrivateMessage(DeletePrivateMessage), DeletePrivateMessage(DeletePrivateMessage),