Refactor activitypub code

This commit is contained in:
Felix Ableitner 2021-03-17 18:12:37 +01:00
parent 71067a8cb5
commit b3a5b4eb82
11 changed files with 158 additions and 207 deletions

View file

@ -10,13 +10,14 @@ use actix_web::web::Data;
use anyhow::Context; use anyhow::Context;
use lemmy_api_structs::{blocking, community::*}; use lemmy_api_structs::{blocking, community::*};
use lemmy_apub::{ use lemmy_apub::{
activities::send::community::{send_add_mod, send_remove_mod},
generate_apub_endpoint, generate_apub_endpoint,
generate_followers_url, generate_followers_url,
generate_inbox_url, generate_inbox_url,
generate_shared_inbox_url, generate_shared_inbox_url,
ActorType, ActorType,
CommunityType,
EndpointType, EndpointType,
UserType,
}; };
use lemmy_db_queries::{ use lemmy_db_queries::{
diesel_option_overwrite_to_url, diesel_option_overwrite_to_url,
@ -745,9 +746,11 @@ impl Perform for AddModToCommunity {
}) })
.await??; .await??;
if data.added { if data.added {
send_add_mod(user, updated_mod, community, context).await?; community.send_add_mod(&user, updated_mod, context).await?;
} else { } else {
send_remove_mod(user, updated_mod, community, context).await?; community
.send_remove_mod(&user, updated_mod, context)
.await?;
} }
// Note: in case a remote mod is added, this returns the old moderators list, it will only get // Note: in case a remote mod is added, this returns the old moderators list, it will only get

View file

@ -1,6 +1,6 @@
use crate::{activities::receive::get_actor_as_user, objects::FromApub, ActorType, NoteExt}; use crate::{activities::receive::get_actor_as_user, objects::FromApub, ActorType, NoteExt};
use activitystreams::{ use activitystreams::{
activity::{ActorAndObjectRefExt, Create, Dislike, Like, Remove, Update}, activity::{ActorAndObjectRefExt, Create, Dislike, Like, Update},
base::ExtendsExt, base::ExtendsExt,
}; };
use anyhow::Context; use anyhow::Context;
@ -221,7 +221,6 @@ pub(crate) async fn receive_delete_comment(
pub(crate) async fn receive_remove_comment( pub(crate) async fn receive_remove_comment(
context: &LemmyContext, context: &LemmyContext,
_remove: Remove,
comment: Comment, comment: Comment,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let removed_comment = blocking(context.pool(), move |conn| { let removed_comment = blocking(context.pool(), move |conn| {

View file

@ -1,19 +1,9 @@
use crate::{
activities::receive::verify_activity_domains_valid,
inbox::verify_is_addressed_to_public,
};
use activitystreams::{
activity::{ActorAndObjectRefExt, Delete, Remove, Undo},
base::{AnyBase, ExtendsExt},
};
use anyhow::Context;
use lemmy_api_structs::{blocking, community::CommunityResponse}; use lemmy_api_structs::{blocking, community::CommunityResponse};
use lemmy_db_queries::{source::community::Community_, ApubObject}; use lemmy_db_queries::source::community::Community_;
use lemmy_db_schema::source::community::Community; use lemmy_db_schema::source::community::Community;
use lemmy_db_views_actor::community_view::CommunityView; use lemmy_db_views_actor::community_view::CommunityView;
use lemmy_utils::{location_info, LemmyError}; use lemmy_utils::LemmyError;
use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext, UserOperation}; use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext, UserOperation};
use url::Url;
pub(crate) async fn receive_delete_community( pub(crate) async fn receive_delete_community(
context: &LemmyContext, context: &LemmyContext,
@ -45,23 +35,8 @@ pub(crate) async fn receive_delete_community(
pub(crate) async fn receive_remove_community( pub(crate) async fn receive_remove_community(
context: &LemmyContext, context: &LemmyContext,
activity: AnyBase, community: Community,
expected_domain: &Url,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let remove = Remove::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&remove, expected_domain, true)?;
verify_is_addressed_to_public(&remove)?;
let community_uri = remove
.object()
.to_owned()
.single_xsd_any_uri()
.context(location_info!())?;
let community = blocking(context.pool(), move |conn| {
Community::read_from_apub_id(conn, &community_uri.into())
})
.await??;
let removed_community = blocking(context.pool(), move |conn| { let removed_community = blocking(context.pool(), move |conn| {
Community::update_removed(conn, community.id, true) Community::update_removed(conn, community.id, true)
}) })
@ -88,16 +63,8 @@ pub(crate) async fn receive_remove_community(
pub(crate) async fn receive_undo_delete_community( pub(crate) async fn receive_undo_delete_community(
context: &LemmyContext, context: &LemmyContext,
undo: Undo,
community: Community, community: Community,
expected_domain: &Url,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_is_addressed_to_public(&undo)?;
let inner = undo.object().to_owned().one().context(location_info!())?;
let delete = Delete::from_any_base(inner)?.context(location_info!())?;
verify_activity_domains_valid(&delete, expected_domain, true)?;
verify_is_addressed_to_public(&delete)?;
let deleted_community = blocking(context.pool(), move |conn| { let deleted_community = blocking(context.pool(), move |conn| {
Community::update_deleted(conn, community.id, false) Community::update_deleted(conn, community.id, false)
}) })
@ -124,26 +91,8 @@ pub(crate) async fn receive_undo_delete_community(
pub(crate) async fn receive_undo_remove_community( pub(crate) async fn receive_undo_remove_community(
context: &LemmyContext, context: &LemmyContext,
undo: Undo, community: Community,
expected_domain: &Url,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_is_addressed_to_public(&undo)?;
let inner = undo.object().to_owned().one().context(location_info!())?;
let remove = Remove::from_any_base(inner)?.context(location_info!())?;
verify_activity_domains_valid(&remove, &expected_domain, true)?;
verify_is_addressed_to_public(&remove)?;
let community_uri = remove
.object()
.to_owned()
.single_xsd_any_uri()
.context(location_info!())?;
let community = blocking(context.pool(), move |conn| {
Community::read_from_apub_id(conn, &community_uri.into())
})
.await??;
let removed_community = blocking(context.pool(), move |conn| { let removed_community = blocking(context.pool(), move |conn| {
Community::update_removed(conn, community.id, false) Community::update_removed(conn, community.id, false)
}) })

View file

@ -6,7 +6,7 @@ use crate::{
PageExt, PageExt,
}; };
use activitystreams::{ use activitystreams::{
activity::{Announce, Create, Dislike, Like, Remove, Update}, activity::{Announce, Create, Dislike, Like, Update},
prelude::*, prelude::*,
}; };
use anyhow::Context; use anyhow::Context;
@ -216,7 +216,6 @@ pub(crate) async fn receive_delete_post(
pub(crate) async fn receive_remove_post( pub(crate) async fn receive_remove_post(
context: &LemmyContext, context: &LemmyContext,
_remove: Remove,
post: Post, post: Post,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let removed_post = blocking(context.pool(), move |conn| { let removed_post = blocking(context.pool(), move |conn| {

View file

@ -6,6 +6,7 @@ use crate::{
fetcher::user::get_or_fetch_and_upsert_user, fetcher::user::get_or_fetch_and_upsert_user,
generate_moderators_url, generate_moderators_url,
ActorType, ActorType,
CommunityType,
}; };
use activitystreams::{ use activitystreams::{
activity::{ activity::{
@ -56,23 +57,10 @@ impl ActorType for Community {
.unwrap_or_else(|| self.inbox_url.to_owned()) .unwrap_or_else(|| self.inbox_url.to_owned())
.into() .into()
} }
async fn send_follow(
&self,
_follow_actor_id: &Url,
_context: &LemmyContext,
) -> Result<(), LemmyError> {
unimplemented!()
}
async fn send_unfollow(
&self,
_follow_actor_id: &Url,
_context: &LemmyContext,
) -> Result<(), LemmyError> {
unimplemented!()
} }
#[async_trait::async_trait(?Send)]
impl CommunityType for Community {
/// As a local community, accept the follow request from a remote user. /// As a local community, accept the follow request from a remote user.
async fn send_accept_follow( async fn send_accept_follow(
&self, &self,
@ -177,7 +165,7 @@ impl ActorType for Community {
.set_many_contexts(lemmy_context()?) .set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(AnnounceType::Announce)?) .set_id(generate_activity_id(AnnounceType::Announce)?)
.set_to(public()) .set_to(public())
.set_many_ccs(vec![self.followers_url.clone().into_inner()]); .set_many_ccs(vec![self.actor_id()]);
send_to_community_followers(announce, self, context).await?; send_to_community_followers(announce, self, context).await?;
@ -204,12 +192,11 @@ impl ActorType for Community {
Ok(inboxes) Ok(inboxes)
} }
}
pub async fn send_add_mod( async fn send_add_mod(
actor: User_, &self,
actor: &User_,
added_mod: User_, added_mod: User_,
community: Community,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let mut add = Add::new( let mut add = Add::new(
@ -220,23 +207,17 @@ pub async fn send_add_mod(
.set_many_contexts(lemmy_context()?) .set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(AddType::Add)?) .set_id(generate_activity_id(AddType::Add)?)
.set_to(public()) .set_to(public())
.set_many_ccs(vec![community.actor_id()]) .set_many_ccs(vec![self.actor_id()])
.set_target(generate_moderators_url(&community.actor_id)?.into_inner()); .set_target(generate_moderators_url(&self.actor_id)?.into_inner());
if community.local { send_to_community(add, actor, self, context).await?;
community
.send_announce(add.into_any_base()?, context)
.await?;
} else {
send_to_community(add, &actor, &community, context).await?;
}
Ok(()) Ok(())
} }
pub async fn send_remove_mod( async fn send_remove_mod(
actor: User_, &self,
actor: &User_,
removed_mod: User_, removed_mod: User_,
community: Community,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let mut remove = Remove::new( let mut remove = Remove::new(
@ -247,15 +228,10 @@ pub async fn send_remove_mod(
.set_many_contexts(lemmy_context()?) .set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(RemoveType::Remove)?) .set_id(generate_activity_id(RemoveType::Remove)?)
.set_to(public()) .set_to(public())
.set_many_ccs(vec![community.actor_id()]) .set_many_ccs(vec![self.actor_id()])
.set_target(generate_moderators_url(&community.actor_id)?.into_inner()); .set_target(generate_moderators_url(&self.actor_id)?.into_inner());
if community.local { send_to_community(remove, &actor, self, context).await?;
community
.send_announce(remove.into_any_base()?, context)
.await?;
} else {
send_to_community(remove, &actor, &community, context).await?;
}
Ok(()) Ok(())
} }
}

View file

@ -3,6 +3,7 @@ use crate::{
activity_queue::send_activity_single_dest, activity_queue::send_activity_single_dest,
extensions::context::lemmy_context, extensions::context::lemmy_context,
ActorType, ActorType,
UserType,
}; };
use activitystreams::{ use activitystreams::{
activity::{ activity::{
@ -10,11 +11,11 @@ use activitystreams::{
Follow, Follow,
Undo, Undo,
}, },
base::{AnyBase, BaseExt, ExtendsExt}, base::{BaseExt, ExtendsExt},
object::ObjectExt, object::ObjectExt,
}; };
use lemmy_api_structs::blocking; use lemmy_api_structs::blocking;
use lemmy_db_queries::{ApubObject, DbPool, Followable}; use lemmy_db_queries::{ApubObject, Followable};
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
community::{Community, CommunityFollower, CommunityFollowerForm}, community::{Community, CommunityFollower, CommunityFollowerForm},
user::User_, user::User_,
@ -47,7 +48,10 @@ impl ActorType for User_ {
.unwrap_or_else(|| self.inbox_url.to_owned()) .unwrap_or_else(|| self.inbox_url.to_owned())
.into() .into()
} }
}
#[async_trait::async_trait(?Send)]
impl UserType for User_ {
/// As a given local user, send out a follow request to a remote community. /// As a given local user, send out a follow request to a remote community.
async fn send_follow( async fn send_follow(
&self, &self,
@ -110,40 +114,4 @@ impl ActorType for User_ {
send_activity_single_dest(undo, self, community.inbox_url.into(), context).await?; send_activity_single_dest(undo, self, community.inbox_url.into(), context).await?;
Ok(()) Ok(())
} }
async fn send_accept_follow(
&self,
_follow: Follow,
_context: &LemmyContext,
) -> Result<(), LemmyError> {
unimplemented!()
}
async fn send_delete(&self, _context: &LemmyContext) -> Result<(), LemmyError> {
unimplemented!()
}
async fn send_undo_delete(&self, _context: &LemmyContext) -> Result<(), LemmyError> {
unimplemented!()
}
async fn send_remove(&self, _context: &LemmyContext) -> Result<(), LemmyError> {
unimplemented!()
}
async fn send_undo_remove(&self, _context: &LemmyContext) -> Result<(), LemmyError> {
unimplemented!()
}
async fn send_announce(
&self,
_activity: AnyBase,
_context: &LemmyContext,
) -> Result<(), LemmyError> {
unimplemented!()
}
async fn get_follower_inboxes(&self, _pool: &DbPool) -> Result<Vec<Url>, LemmyError> {
unimplemented!()
}
} }

View file

@ -3,6 +3,7 @@ use crate::{
extensions::signatures::sign_and_send, extensions::signatures::sign_and_send,
insert_activity, insert_activity,
ActorType, ActorType,
CommunityType,
APUB_JSON_CONTENT_TYPE, APUB_JSON_CONTENT_TYPE,
}; };
use activitystreams::{ use activitystreams::{

View file

@ -20,6 +20,7 @@ use crate::{
}, },
insert_activity, insert_activity,
ActorType, ActorType,
CommunityType,
}; };
use activitystreams::{ use activitystreams::{
activity::{kind::FollowType, ActorAndObject, Follow, Undo}, activity::{kind::FollowType, ActorAndObject, Follow, Undo},

View file

@ -35,10 +35,13 @@ use crate::{
objects::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post}, objects::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
user::get_or_fetch_and_upsert_user, user::get_or_fetch_and_upsert_user,
}, },
find_object_by_id,
find_post_or_comment_by_id, find_post_or_comment_by_id,
generate_moderators_url, generate_moderators_url,
inbox::verify_is_addressed_to_public, inbox::verify_is_addressed_to_public,
ActorType, ActorType,
CommunityType,
Object,
PostOrComment, PostOrComment,
}; };
use activitystreams::{ use activitystreams::{
@ -254,8 +257,8 @@ pub(in crate::inbox) async fn receive_remove_for_community(
.context(location_info!())?; .context(location_info!())?;
match find_post_or_comment_by_id(context, object).await { match find_post_or_comment_by_id(context, object).await {
Ok(PostOrComment::Post(p)) => receive_remove_post(context, remove, *p).await, Ok(PostOrComment::Post(p)) => receive_remove_post(context, *p).await,
Ok(PostOrComment::Comment(c)) => receive_remove_comment(context, remove, *c).await, Ok(PostOrComment::Comment(c)) => receive_remove_comment(context, *c).await,
// if we dont have the object, no need to do anything // if we dont have the object, no need to do anything
Err(_) => Ok(()), Err(_) => Ok(()),
} }
@ -600,9 +603,12 @@ where
.map(|o| o.id()) .map(|o| o.id())
.flatten() .flatten()
.context(location_info!())?; .context(location_info!())?;
let original_id = match find_post_or_comment_by_id(context, object_id.to_owned()).await? { let original_id = match find_object_by_id(context, object_id.to_owned()).await? {
PostOrComment::Post(p) => p.ap_id.into_inner(), Object::Post(p) => p.ap_id.into_inner(),
PostOrComment::Comment(c) => c.ap_id.into_inner(), Object::Comment(c) => c.ap_id.into_inner(),
Object::Community(c) => c.actor_id(),
Object::User(u) => u.actor_id(),
Object::PrivateMessage(p) => p.ap_id.into_inner(),
}; };
if actor_id.domain() != original_id.domain() { if actor_id.domain() != original_id.domain() {
let community = extract_community_from_cc(activity, context).await?; let community = extract_community_from_cc(activity, context).await?;

View file

@ -41,7 +41,7 @@ use crate::{
ActorType, ActorType,
}; };
use activitystreams::{ use activitystreams::{
activity::{Accept, ActorAndObject, Announce, Create, Delete, Follow, Undo, Update}, activity::{Accept, ActorAndObject, Announce, Create, Delete, Follow, Remove, Undo, Update},
base::AnyBase, base::AnyBase,
prelude::*, prelude::*,
}; };
@ -165,7 +165,7 @@ pub(crate) async fn user_receive_message(
receive_delete(context, any_base, &actor_url, request_counter).await? receive_delete(context, any_base, &actor_url, request_counter).await?
} }
UserValidTypes::Undo => receive_undo(context, any_base, &actor_url, request_counter).await?, UserValidTypes::Undo => receive_undo(context, any_base, &actor_url, request_counter).await?,
UserValidTypes::Remove => receive_remove_community(&context, any_base, &actor_url).await?, UserValidTypes::Remove => receive_remove(context, any_base, &actor_url).await?,
}; };
// TODO: would be logical to move websocket notification code here // TODO: would be logical to move websocket notification code here
@ -370,13 +370,31 @@ async fn receive_delete(
} }
} }
async fn receive_remove(
context: &LemmyContext,
any_base: AnyBase,
expected_domain: &Url,
) -> Result<(), LemmyError> {
let remove = Remove::from_any_base(any_base.clone())?.context(location_info!())?;
verify_activity_domains_valid(&remove, expected_domain, true)?;
let object_uri = remove
.object()
.to_owned()
.single_xsd_any_uri()
.context(location_info!())?;
let community = blocking(context.pool(), move |conn| {
Community::read_from_apub_id(conn, &object_uri.into())
})
.await??;
receive_remove_community(&context, community).await
}
async fn receive_undo( async fn receive_undo(
context: &LemmyContext, context: &LemmyContext,
any_base: AnyBase, any_base: AnyBase,
expected_domain: &Url, expected_domain: &Url,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
use CommunityOrPrivateMessage::*;
let undo = Undo::from_any_base(any_base)?.context(location_info!())?; let undo = Undo::from_any_base(any_base)?.context(location_info!())?;
verify_activity_domains_valid(&undo, expected_domain, true)?; verify_activity_domains_valid(&undo, expected_domain, true)?;
@ -391,15 +409,28 @@ async fn receive_undo(
.to_owned() .to_owned()
.single_xsd_any_uri() .single_xsd_any_uri()
.context(location_info!())?; .context(location_info!())?;
use CommunityOrPrivateMessage::*;
match find_community_or_private_message_by_id(context, object_uri).await? { match find_community_or_private_message_by_id(context, object_uri).await? {
Community(c) => receive_undo_delete_community(context, undo, c, expected_domain).await, Community(c) => receive_undo_delete_community(context, c).await,
PrivateMessage(p) => { PrivateMessage(p) => {
receive_undo_delete_private_message(context, undo, expected_domain, p, request_counter) receive_undo_delete_private_message(context, undo, expected_domain, p, request_counter)
.await .await
} }
} }
} }
Some("Remove") => receive_undo_remove_community(context, undo, expected_domain).await, Some("Remove") => {
let remove = Remove::from_any_base(inner_activity)?.context(location_info!())?;
let object_uri = remove
.object()
.to_owned()
.single_xsd_any_uri()
.context(location_info!())?;
let community = blocking(context.pool(), move |conn| {
Community::read_from_apub_id(conn, &object_uri.into())
})
.await??;
receive_undo_remove_community(context, community).await
}
_ => receive_unhandled_activity(undo), _ => receive_unhandled_activity(undo),
} }
} }

View file

@ -152,38 +152,6 @@ pub trait ActorType {
fn public_key(&self) -> Option<String>; fn public_key(&self) -> Option<String>;
fn private_key(&self) -> Option<String>; fn private_key(&self) -> Option<String>;
async fn send_follow(
&self,
follow_actor_id: &Url,
context: &LemmyContext,
) -> Result<(), LemmyError>;
async fn send_unfollow(
&self,
follow_actor_id: &Url,
context: &LemmyContext,
) -> Result<(), LemmyError>;
async fn send_accept_follow(
&self,
follow: Follow,
context: &LemmyContext,
) -> Result<(), LemmyError>;
async fn send_delete(&self, context: &LemmyContext) -> Result<(), LemmyError>;
async fn send_undo_delete(&self, context: &LemmyContext) -> Result<(), LemmyError>;
async fn send_remove(&self, context: &LemmyContext) -> Result<(), LemmyError>;
async fn send_undo_remove(&self, context: &LemmyContext) -> Result<(), LemmyError>;
async fn send_announce(
&self,
activity: AnyBase,
context: &LemmyContext,
) -> Result<(), LemmyError>;
/// For a given community, returns the inboxes of all followers.
async fn get_follower_inboxes(&self, pool: &DbPool) -> Result<Vec<Url>, LemmyError>;
fn get_shared_inbox_or_inbox_url(&self) -> Url; fn get_shared_inbox_or_inbox_url(&self) -> Url;
/// Outbox URL is not generally used by Lemmy, so it can be generated on the fly (but only for /// Outbox URL is not generally used by Lemmy, so it can be generated on the fly (but only for
@ -207,6 +175,55 @@ pub trait ActorType {
} }
} }
#[async_trait::async_trait(?Send)]
pub trait CommunityType {
async fn get_follower_inboxes(&self, pool: &DbPool) -> Result<Vec<Url>, LemmyError>;
async fn send_accept_follow(
&self,
follow: Follow,
context: &LemmyContext,
) -> Result<(), LemmyError>;
async fn send_delete(&self, context: &LemmyContext) -> Result<(), LemmyError>;
async fn send_undo_delete(&self, context: &LemmyContext) -> Result<(), LemmyError>;
async fn send_remove(&self, context: &LemmyContext) -> Result<(), LemmyError>;
async fn send_undo_remove(&self, context: &LemmyContext) -> Result<(), LemmyError>;
async fn send_announce(
&self,
activity: AnyBase,
context: &LemmyContext,
) -> Result<(), LemmyError>;
async fn send_add_mod(
&self,
actor: &User_,
added_mod: User_,
context: &LemmyContext,
) -> Result<(), LemmyError>;
async fn send_remove_mod(
&self,
actor: &User_,
removed_mod: User_,
context: &LemmyContext,
) -> Result<(), LemmyError>;
}
#[async_trait::async_trait(?Send)]
pub trait UserType {
async fn send_follow(
&self,
follow_actor_id: &Url,
context: &LemmyContext,
) -> Result<(), LemmyError>;
async fn send_unfollow(
&self,
follow_actor_id: &Url,
context: &LemmyContext,
) -> Result<(), LemmyError>;
}
pub enum EndpointType { pub enum EndpointType {
Community, Community,
User, User,
@ -319,6 +336,7 @@ pub(crate) async fn find_post_or_comment_by_id(
Err(NotFound.into()) Err(NotFound.into())
} }
#[derive(Debug)]
pub(crate) enum Object { pub(crate) enum Object {
Comment(Comment), Comment(Comment),
Post(Post), Post(Post),