mirror of
https://github.com/LemmyNet/lemmy
synced 2024-11-14 08:47:14 +00:00
convert remaining activity receivers
This commit is contained in:
parent
e1e54672c6
commit
8a071b7b07
52 changed files with 456 additions and 1012 deletions
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
activities_new::comment::{get_notif_recipients, send_websocket_message},
|
||||
activities::comment::{get_notif_recipients, send_websocket_message},
|
||||
inbox::new_inbox_routing::Activity,
|
||||
};
|
||||
use activitystreams::{activity::kind::CreateType, base::BaseExt};
|
||||
|
@ -10,7 +10,7 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateComment {
|
||||
actor: Url,
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{activities_new::comment::send_websocket_message, inbox::new_inbox_routing::Activity};
|
||||
use crate::{activities::comment::send_websocket_message, inbox::new_inbox_routing::Activity};
|
||||
use activitystreams::activity::kind::DeleteType;
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_apub::{check_is_apub_id_valid, fetcher::objects::get_or_fetch_and_insert_comment};
|
||||
|
@ -9,12 +9,12 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DeleteComment {
|
||||
pub(in crate::activities_new::comment) actor: Url,
|
||||
pub(in crate::activities::comment) actor: Url,
|
||||
to: PublicUrl,
|
||||
pub(in crate::activities_new::comment) object: Url,
|
||||
pub(in crate::activities::comment) object: Url,
|
||||
cc: [Url; 1],
|
||||
#[serde(rename = "type")]
|
||||
kind: DeleteType,
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{activities_new::comment::like_or_dislike_comment, inbox::new_inbox_routing::Activity};
|
||||
use crate::{activities::comment::like_or_dislike_comment, inbox::new_inbox_routing::Activity};
|
||||
use activitystreams::activity::kind::DislikeType;
|
||||
use lemmy_apub::check_is_apub_id_valid;
|
||||
use lemmy_apub_lib::{verify_domains_match, PublicUrl, ReceiveActivity, VerifyActivity};
|
||||
|
@ -6,12 +6,12 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DislikeComment {
|
||||
actor: Url,
|
||||
to: PublicUrl,
|
||||
pub(in crate::activities_new::comment) object: Url,
|
||||
pub(in crate::activities::comment) object: Url,
|
||||
cc: [Url; 1],
|
||||
#[serde(rename = "type")]
|
||||
kind: DislikeType,
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{activities_new::comment::like_or_dislike_comment, inbox::new_inbox_routing::Activity};
|
||||
use crate::{activities::comment::like_or_dislike_comment, inbox::new_inbox_routing::Activity};
|
||||
use activitystreams::activity::kind::LikeType;
|
||||
use lemmy_apub::check_is_apub_id_valid;
|
||||
use lemmy_apub_lib::{verify_domains_match, PublicUrl, ReceiveActivity, VerifyActivity};
|
||||
|
@ -6,12 +6,12 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct LikeComment {
|
||||
actor: Url,
|
||||
to: PublicUrl,
|
||||
pub(in crate::activities_new::comment) object: Url,
|
||||
pub(in crate::activities::comment) object: Url,
|
||||
cc: [Url; 1],
|
||||
#[serde(rename = "type")]
|
||||
kind: LikeType,
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
activities_new::{comment::send_websocket_message, verify_mod_action},
|
||||
activities::{comment::send_websocket_message, verify_mod_action},
|
||||
inbox::new_inbox_routing::Activity,
|
||||
};
|
||||
use activitystreams::activity::kind::RemoveType;
|
||||
|
@ -12,12 +12,12 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RemoveComment {
|
||||
actor: Url,
|
||||
to: PublicUrl,
|
||||
pub(in crate::activities_new::comment) object: Url,
|
||||
pub(in crate::activities::comment) object: Url,
|
||||
cc: [Url; 1],
|
||||
#[serde(rename = "type")]
|
||||
kind: RemoveType,
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
activities_new::comment::{delete::DeleteComment, send_websocket_message},
|
||||
activities::comment::{delete::DeleteComment, send_websocket_message},
|
||||
inbox::new_inbox_routing::Activity,
|
||||
};
|
||||
use activitystreams::activity::kind::UndoType;
|
||||
|
@ -12,7 +12,7 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UndoDeleteComment {
|
||||
actor: Url,
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
activities_new::comment::{dislike::DislikeComment, undo_like_or_dislike_comment},
|
||||
activities::comment::{dislike::DislikeComment, undo_like_or_dislike_comment},
|
||||
inbox::new_inbox_routing::Activity,
|
||||
};
|
||||
use activitystreams::activity::kind::UndoType;
|
||||
|
@ -9,7 +9,7 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UndoDislikeComment {
|
||||
actor: Url,
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
activities_new::comment::{like::LikeComment, undo_like_or_dislike_comment},
|
||||
activities::comment::{like::LikeComment, undo_like_or_dislike_comment},
|
||||
inbox::new_inbox_routing::Activity,
|
||||
};
|
||||
use activitystreams::activity::kind::UndoType;
|
||||
|
@ -9,7 +9,7 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UndoLikeComment {
|
||||
actor: Url,
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
activities_new::{
|
||||
activities::{
|
||||
comment::{remove::RemoveComment, send_websocket_message},
|
||||
verify_mod_action,
|
||||
},
|
||||
|
@ -15,7 +15,7 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UndoRemoveComment {
|
||||
actor: Url,
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
activities_new::comment::{get_notif_recipients, send_websocket_message},
|
||||
activities::comment::{get_notif_recipients, send_websocket_message},
|
||||
inbox::new_inbox_routing::Activity,
|
||||
};
|
||||
use activitystreams::{activity::kind::UpdateType, base::BaseExt};
|
||||
|
@ -10,7 +10,7 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdateComment {
|
||||
actor: Url,
|
80
crates/apub_receive/src/activities/community/add_mod.rs
Normal file
80
crates/apub_receive/src/activities/community/add_mod.rs
Normal file
|
@ -0,0 +1,80 @@
|
|||
use crate::{
|
||||
activities::{community::verify_add_remove_moderator_target, verify_mod_action},
|
||||
inbox::new_inbox_routing::Activity,
|
||||
};
|
||||
use activitystreams::{activity::kind::AddType, base::AnyBase};
|
||||
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, PublicUrl, ReceiveActivity, VerifyActivity};
|
||||
use lemmy_db_queries::{source::community::CommunityModerator_, Joinable};
|
||||
use lemmy_db_schema::source::community::{CommunityModerator, CommunityModeratorForm};
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AddModToCommunity {
|
||||
actor: Url,
|
||||
to: PublicUrl,
|
||||
object: Url,
|
||||
target: Url,
|
||||
cc: [Url; 1],
|
||||
#[serde(rename = "type")]
|
||||
kind: AddType,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl VerifyActivity for Activity<AddModToCommunity> {
|
||||
async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||
verify_domains_match(&self.inner.actor, self.id_unchecked())?;
|
||||
verify_domains_match(&self.inner.target, &self.inner.cc[0])?;
|
||||
check_is_apub_id_valid(&self.inner.actor, false)?;
|
||||
verify_mod_action(self.inner.actor.clone(), self.inner.cc[0].clone(), context).await?;
|
||||
verify_add_remove_moderator_target(&self.inner.target, self.inner.cc[0].clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ReceiveActivity for Activity<AddModToCommunity> {
|
||||
async fn receive(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let community =
|
||||
get_or_fetch_and_upsert_community(&self.inner.cc[0], context, request_counter).await?;
|
||||
let new_mod =
|
||||
get_or_fetch_and_upsert_person(&self.inner.object, context, request_counter).await?;
|
||||
|
||||
// If we had to refetch the community while parsing the activity, then the new mod has already
|
||||
// been added. Skip it here as it would result in a duplicate key error.
|
||||
let new_mod_id = new_mod.id;
|
||||
let moderated_communities = blocking(context.pool(), move |conn| {
|
||||
CommunityModerator::get_person_moderated_communities(conn, new_mod_id)
|
||||
})
|
||||
.await??;
|
||||
if !moderated_communities.contains(&community.id) {
|
||||
let form = CommunityModeratorForm {
|
||||
community_id: community.id,
|
||||
person_id: new_mod.id,
|
||||
};
|
||||
blocking(context.pool(), move |conn| {
|
||||
CommunityModerator::join(conn, &form)
|
||||
})
|
||||
.await??;
|
||||
}
|
||||
if community.local {
|
||||
let anybase = AnyBase::from_arbitrary_json(serde_json::to_string(self)?)?;
|
||||
community
|
||||
.send_announce(anybase, Some(self.inner.object.clone()), context)
|
||||
.await?;
|
||||
}
|
||||
// TODO: send websocket notification about added mod
|
||||
Ok(())
|
||||
}
|
||||
}
|
134
crates/apub_receive/src/activities/community/announce.rs
Normal file
134
crates/apub_receive/src/activities/community/announce.rs
Normal file
|
@ -0,0 +1,134 @@
|
|||
use crate::{
|
||||
activities::{
|
||||
comment::{
|
||||
create::CreateComment,
|
||||
delete::DeleteComment,
|
||||
dislike::DislikeComment,
|
||||
like::LikeComment,
|
||||
remove::RemoveComment,
|
||||
undo_delete::UndoDeleteComment,
|
||||
undo_dislike::UndoDislikeComment,
|
||||
undo_like::UndoLikeComment,
|
||||
undo_remove::UndoRemoveComment,
|
||||
update::UpdateComment,
|
||||
},
|
||||
community::{
|
||||
block_user::BlockUserFromCommunity,
|
||||
delete::DeleteCommunity,
|
||||
remove::RemoveCommunity,
|
||||
undo_block_user::UndoBlockUserFromCommunity,
|
||||
undo_delete::UndoDeleteCommunity,
|
||||
undo_remove::UndoRemoveCommunity,
|
||||
update::UpdateCommunity,
|
||||
},
|
||||
post::{
|
||||
create::CreatePost,
|
||||
delete::DeletePost,
|
||||
dislike::DislikePost,
|
||||
like::LikePost,
|
||||
remove::RemovePost,
|
||||
undo_delete::UndoDeletePost,
|
||||
undo_dislike::UndoDislikePost,
|
||||
undo_like::UndoLikePost,
|
||||
undo_remove::UndoRemovePost,
|
||||
update::UpdatePost,
|
||||
},
|
||||
},
|
||||
inbox::{is_activity_already_known, new_inbox_routing::Activity},
|
||||
};
|
||||
use activitystreams::activity::kind::RemoveType;
|
||||
use lemmy_apub::check_is_apub_id_valid;
|
||||
use lemmy_apub_lib::{verify_domains_match, PublicUrl, ReceiveActivity, VerifyActivity};
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub enum AnnouncableActivities {
|
||||
CreateComment(CreateComment),
|
||||
UpdateComment(UpdateComment),
|
||||
LikeComment(LikeComment),
|
||||
DislikeComment(DislikeComment),
|
||||
UndoLikeComment(UndoLikeComment),
|
||||
UndoDislikeComment(UndoDislikeComment),
|
||||
DeleteComment(DeleteComment),
|
||||
UndoDeleteComment(UndoDeleteComment),
|
||||
RemoveComment(RemoveComment),
|
||||
UndoRemoveComment(UndoRemoveComment),
|
||||
CreatePost(CreatePost),
|
||||
UpdatePost(UpdatePost),
|
||||
LikePost(LikePost),
|
||||
DislikePost(DislikePost),
|
||||
DeletePost(DeletePost),
|
||||
UndoDeletePost(UndoDeletePost),
|
||||
RemovePost(RemovePost),
|
||||
UndoRemovePost(UndoRemovePost),
|
||||
UndoLikePost(UndoLikePost),
|
||||
UndoDislikePost(UndoDislikePost),
|
||||
// TODO: which of these get announced?
|
||||
UpdateCommunity(UpdateCommunity),
|
||||
DeleteCommunity(DeleteCommunity),
|
||||
RemoveCommunity(RemoveCommunity),
|
||||
UndoDeleteCommunity(UndoDeleteCommunity),
|
||||
UndoRemoveCommunity(UndoRemoveCommunity),
|
||||
BlockUserFromCommunity(BlockUserFromCommunity),
|
||||
UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl VerifyActivity for AnnouncableActivities {
|
||||
async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||
self.verify(context).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ReceiveActivity for AnnouncableActivities {
|
||||
async fn receive(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
self.receive(context, request_counter).await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AnnounceActivity {
|
||||
actor: Url,
|
||||
to: PublicUrl,
|
||||
object: Activity<AnnouncableActivities>,
|
||||
cc: [Url; 1],
|
||||
#[serde(rename = "type")]
|
||||
kind: RemoveType,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl VerifyActivity for Activity<AnnounceActivity> {
|
||||
async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||
verify_domains_match(&self.inner.actor, self.id_unchecked())?;
|
||||
verify_domains_match(&self.inner.actor, &self.inner.cc[0])?;
|
||||
check_is_apub_id_valid(&self.inner.actor, false)?;
|
||||
self.inner.object.inner.verify(context).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ReceiveActivity for Activity<AnnounceActivity> {
|
||||
async fn receive(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
if is_activity_already_known(context.pool(), &self.inner.object.id_unchecked()).await? {
|
||||
return Ok(());
|
||||
}
|
||||
self
|
||||
.inner
|
||||
.object
|
||||
.inner
|
||||
.receive(context, request_counter)
|
||||
.await
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{activities_new::verify_mod_action, inbox::new_inbox_routing::Activity};
|
||||
use crate::{activities::verify_mod_action, inbox::new_inbox_routing::Activity};
|
||||
use activitystreams::activity::kind::BlockType;
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_apub::{
|
||||
|
@ -17,12 +17,12 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BlockUserFromCommunity {
|
||||
actor: Url,
|
||||
to: PublicUrl,
|
||||
pub(in crate::activities_new::community) object: Url,
|
||||
pub(in crate::activities::community) object: Url,
|
||||
cc: [Url; 1],
|
||||
#[serde(rename = "type")]
|
||||
kind: BlockType,
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
activities_new::community::{send_websocket_message, verify_is_community_mod},
|
||||
activities::community::{send_websocket_message, verify_is_community_mod},
|
||||
inbox::new_inbox_routing::Activity,
|
||||
};
|
||||
use activitystreams::activity::kind::DeleteType;
|
||||
|
@ -20,12 +20,12 @@ use url::Url;
|
|||
// We have two possibilities which need to be handled:
|
||||
// 1. actor is remote mod, community id in object
|
||||
// 2. actor is community, cc is followers collection
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DeleteCommunity {
|
||||
actor: Url,
|
||||
to: PublicUrl,
|
||||
pub(in crate::activities_new::community) object: Url,
|
||||
pub(in crate::activities::community) object: Url,
|
||||
cc: [Url; 1],
|
||||
#[serde(rename = "type")]
|
||||
kind: DeleteType,
|
|
@ -1,5 +1,6 @@
|
|||
use anyhow::anyhow;
|
||||
use lemmy_api_common::{blocking, community::CommunityResponse};
|
||||
use lemmy_apub::generate_moderators_url;
|
||||
use lemmy_db_queries::ApubObject;
|
||||
use lemmy_db_schema::{
|
||||
source::{community::Community, person::Person},
|
||||
|
@ -10,9 +11,12 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext};
|
||||
use url::Url;
|
||||
|
||||
pub mod add_mod;
|
||||
pub mod announce;
|
||||
pub mod block_user;
|
||||
pub mod delete;
|
||||
pub mod remove;
|
||||
pub mod remove_mod;
|
||||
pub mod undo_block_user;
|
||||
pub mod undo_delete;
|
||||
pub mod undo_remove;
|
||||
|
@ -63,3 +67,12 @@ async fn verify_is_community_mod(
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// For Add/Remove community moderator activities, check that the target field actually contains
|
||||
/// /c/community/moderators. Any different values are unsupported.
|
||||
fn verify_add_remove_moderator_target(target: &Url, community: Url) -> Result<(), LemmyError> {
|
||||
if target != &generate_moderators_url(&community.into())?.into_inner() {
|
||||
return Err(anyhow!("Unkown target url").into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
|
@ -1,7 +1,4 @@
|
|||
use crate::{
|
||||
activities_new::community::send_websocket_message,
|
||||
inbox::new_inbox_routing::Activity,
|
||||
};
|
||||
use crate::{activities::community::send_websocket_message, inbox::new_inbox_routing::Activity};
|
||||
use activitystreams::activity::kind::RemoveType;
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_apub::check_is_apub_id_valid;
|
||||
|
@ -12,12 +9,12 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RemoveCommunity {
|
||||
actor: Url,
|
||||
to: PublicUrl,
|
||||
pub(in crate::activities_new::community) object: Url,
|
||||
pub(in crate::activities::community) object: Url,
|
||||
cc: [Url; 1],
|
||||
#[serde(rename = "type")]
|
||||
kind: RemoveType,
|
69
crates/apub_receive/src/activities/community/remove_mod.rs
Normal file
69
crates/apub_receive/src/activities/community/remove_mod.rs
Normal file
|
@ -0,0 +1,69 @@
|
|||
use crate::{
|
||||
activities::{community::verify_add_remove_moderator_target, verify_mod_action},
|
||||
inbox::new_inbox_routing::Activity,
|
||||
};
|
||||
use activitystreams::{activity::kind::RemoveType, base::AnyBase};
|
||||
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, PublicUrl, ReceiveActivity, VerifyActivity};
|
||||
use lemmy_db_queries::Joinable;
|
||||
use lemmy_db_schema::source::community::{CommunityModerator, CommunityModeratorForm};
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RemoveModToCommunity {
|
||||
actor: Url,
|
||||
to: PublicUrl,
|
||||
object: Url,
|
||||
target: Url,
|
||||
cc: [Url; 1],
|
||||
#[serde(rename = "type")]
|
||||
kind: RemoveType,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl VerifyActivity for Activity<RemoveModToCommunity> {
|
||||
async fn verify(&self, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||
verify_domains_match(&self.inner.actor, self.id_unchecked())?;
|
||||
verify_domains_match(&self.inner.target, &self.inner.cc[0])?;
|
||||
check_is_apub_id_valid(&self.inner.actor, false)?;
|
||||
verify_mod_action(self.inner.actor.clone(), self.inner.cc[0].clone(), context).await?;
|
||||
verify_add_remove_moderator_target(&self.inner.target, self.inner.cc[0].clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ReceiveActivity for Activity<RemoveModToCommunity> {
|
||||
async fn receive(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let community =
|
||||
get_or_fetch_and_upsert_community(&self.inner.cc[0], context, request_counter).await?;
|
||||
let add_mod =
|
||||
get_or_fetch_and_upsert_person(&self.inner.object, context, request_counter).await?;
|
||||
|
||||
let form = CommunityModeratorForm {
|
||||
community_id: community.id,
|
||||
person_id: add_mod.id,
|
||||
};
|
||||
blocking(context.pool(), move |conn| {
|
||||
CommunityModerator::leave(conn, &form)
|
||||
})
|
||||
.await??;
|
||||
let anybase = AnyBase::from_arbitrary_json(serde_json::to_string(self)?)?;
|
||||
community
|
||||
.send_announce(anybase, Some(self.inner.object.clone()), context)
|
||||
.await?;
|
||||
// TODO: send websocket notification about removed mod
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
activities_new::{community::block_user::BlockUserFromCommunity, verify_mod_action},
|
||||
activities::{community::block_user::BlockUserFromCommunity, verify_mod_action},
|
||||
inbox::new_inbox_routing::Activity,
|
||||
};
|
||||
use activitystreams::activity::kind::BlockType;
|
||||
|
@ -15,7 +15,7 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UndoBlockUserFromCommunity {
|
||||
actor: Url,
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
activities_new::community::{
|
||||
activities::community::{
|
||||
delete::DeleteCommunity,
|
||||
send_websocket_message,
|
||||
verify_is_community_mod,
|
||||
|
@ -24,7 +24,7 @@ use url::Url;
|
|||
// We have two possibilities which need to be handled:
|
||||
// 1. actor is remote mod, community id in object
|
||||
// 2. actor is community, cc is followers collection
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UndoDeleteCommunity {
|
||||
actor: Url,
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
activities_new::community::{remove::RemoveCommunity, send_websocket_message},
|
||||
activities::community::{remove::RemoveCommunity, send_websocket_message},
|
||||
inbox::new_inbox_routing::Activity,
|
||||
};
|
||||
use activitystreams::activity::kind::RemoveType;
|
||||
|
@ -12,7 +12,7 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UndoRemoveCommunity {
|
||||
actor: Url,
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
activities_new::community::{send_websocket_message, verify_is_community_mod},
|
||||
activities::community::{send_websocket_message, verify_is_community_mod},
|
||||
inbox::new_inbox_routing::Activity,
|
||||
};
|
||||
use activitystreams::{activity::kind::UpdateType, base::BaseExt};
|
||||
|
@ -14,7 +14,7 @@ use url::Url;
|
|||
|
||||
/// This activity is received from a remote community mod, and updates the description or other
|
||||
/// fields of a local community.
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdateCommunity {
|
||||
actor: Url,
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{activities_new::follow::follow::FollowCommunity, inbox::new_inbox_routing::Activity};
|
||||
use crate::{activities::follow::follow::FollowCommunity, inbox::new_inbox_routing::Activity};
|
||||
use activitystreams::activity::kind::AcceptType;
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_apub::{
|
||||
|
@ -12,7 +12,7 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AcceptFollowCommunity {
|
||||
actor: Url,
|
|
@ -17,12 +17,12 @@ use lemmy_utils::{location_info, LemmyError};
|
|||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct FollowCommunity {
|
||||
actor: Url,
|
||||
to: Url,
|
||||
pub(in crate::activities_new::follow) object: Url,
|
||||
pub(in crate::activities::follow) object: Url,
|
||||
#[serde(rename = "type")]
|
||||
kind: FollowType,
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{activities_new::follow::follow::FollowCommunity, inbox::new_inbox_routing::Activity};
|
||||
use crate::{activities::follow::follow::FollowCommunity, inbox::new_inbox_routing::Activity};
|
||||
use activitystreams::activity::kind::UndoType;
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_apub::{
|
||||
|
@ -12,7 +12,7 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UndoFollowCommunity {
|
||||
actor: Url,
|
|
@ -1 +1,45 @@
|
|||
pub(crate) mod receive;
|
||||
use anyhow::anyhow;
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_db_queries::ApubObject;
|
||||
use lemmy_db_schema::source::{community::Community, person::Person};
|
||||
use lemmy_db_views_actor::community_view::CommunityView;
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
||||
pub mod comment;
|
||||
pub mod community;
|
||||
pub mod follow;
|
||||
pub mod post;
|
||||
pub mod private_message;
|
||||
|
||||
async fn verify_mod_action(
|
||||
actor_id: Url,
|
||||
activity_cc: Url,
|
||||
context: &LemmyContext,
|
||||
) -> Result<(), LemmyError> {
|
||||
let community = blocking(context.pool(), move |conn| {
|
||||
Community::read_from_apub_id(conn, &activity_cc.into())
|
||||
})
|
||||
.await??;
|
||||
|
||||
if community.local {
|
||||
let actor = blocking(&context.pool(), move |conn| {
|
||||
Person::read_from_apub_id(&conn, &actor_id.clone().into())
|
||||
})
|
||||
.await??;
|
||||
|
||||
// Note: this will also return true for admins in addition to mods, but as we dont know about
|
||||
// remote admins, it doesnt make any difference.
|
||||
let community_id = community.id;
|
||||
let actor_id = actor.id;
|
||||
let is_mod_or_admin = blocking(context.pool(), move |conn| {
|
||||
CommunityView::is_mod_or_admin(conn, actor_id, community_id)
|
||||
})
|
||||
.await?;
|
||||
if !is_mod_or_admin {
|
||||
return Err(anyhow!("Not a mod").into());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{activities_new::post::send_websocket_message, inbox::new_inbox_routing::Activity};
|
||||
use crate::{activities::post::send_websocket_message, inbox::new_inbox_routing::Activity};
|
||||
use activitystreams::{activity::kind::CreateType, base::BaseExt};
|
||||
use lemmy_apub::{
|
||||
check_is_apub_id_valid,
|
||||
|
@ -13,7 +13,7 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreatePost {
|
||||
actor: Url,
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{activities_new::post::send_websocket_message, inbox::new_inbox_routing::Activity};
|
||||
use crate::{activities::post::send_websocket_message, inbox::new_inbox_routing::Activity};
|
||||
use activitystreams::activity::kind::DeleteType;
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_apub::{check_is_apub_id_valid, fetcher::objects::get_or_fetch_and_insert_post};
|
||||
|
@ -9,12 +9,12 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DeletePost {
|
||||
pub(in crate::activities_new::post) actor: Url,
|
||||
pub(in crate::activities::post) actor: Url,
|
||||
to: PublicUrl,
|
||||
pub(in crate::activities_new::post) object: Url,
|
||||
pub(in crate::activities::post) object: Url,
|
||||
cc: [Url; 1],
|
||||
#[serde(rename = "type")]
|
||||
kind: DeleteType,
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{activities_new::post::like_or_dislike_post, inbox::new_inbox_routing::Activity};
|
||||
use crate::{activities::post::like_or_dislike_post, inbox::new_inbox_routing::Activity};
|
||||
use activitystreams::activity::kind::DislikeType;
|
||||
use lemmy_apub::check_is_apub_id_valid;
|
||||
use lemmy_apub_lib::{verify_domains_match, PublicUrl, ReceiveActivity, VerifyActivity};
|
||||
|
@ -6,12 +6,12 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DislikePost {
|
||||
actor: Url,
|
||||
to: PublicUrl,
|
||||
pub(in crate::activities_new::post) object: Url,
|
||||
pub(in crate::activities::post) object: Url,
|
||||
cc: [Url; 1],
|
||||
#[serde(rename = "type")]
|
||||
kind: DislikeType,
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{activities_new::post::like_or_dislike_post, inbox::new_inbox_routing::Activity};
|
||||
use crate::{activities::post::like_or_dislike_post, inbox::new_inbox_routing::Activity};
|
||||
use activitystreams::activity::kind::LikeType;
|
||||
use lemmy_apub::check_is_apub_id_valid;
|
||||
use lemmy_apub_lib::{verify_domains_match, PublicUrl, ReceiveActivity, VerifyActivity};
|
||||
|
@ -6,12 +6,12 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct LikePost {
|
||||
actor: Url,
|
||||
to: PublicUrl,
|
||||
pub(in crate::activities_new::post) object: Url,
|
||||
pub(in crate::activities::post) object: Url,
|
||||
cc: [Url; 1],
|
||||
#[serde(rename = "type")]
|
||||
kind: LikeType,
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
activities_new::{post::send_websocket_message, verify_mod_action},
|
||||
activities::{post::send_websocket_message, verify_mod_action},
|
||||
inbox::new_inbox_routing::Activity,
|
||||
};
|
||||
use activitystreams::activity::kind::RemoveType;
|
||||
|
@ -12,12 +12,12 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RemovePost {
|
||||
actor: Url,
|
||||
to: PublicUrl,
|
||||
pub(in crate::activities_new::post) object: Url,
|
||||
pub(in crate::activities::post) object: Url,
|
||||
cc: [Url; 1],
|
||||
#[serde(rename = "type")]
|
||||
kind: RemoveType,
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
activities_new::post::{delete::DeletePost, send_websocket_message},
|
||||
activities::post::{delete::DeletePost, send_websocket_message},
|
||||
inbox::new_inbox_routing::Activity,
|
||||
};
|
||||
use activitystreams::activity::kind::UndoType;
|
||||
|
@ -12,7 +12,7 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UndoDeletePost {
|
||||
actor: Url,
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
activities_new::post::{dislike::DislikePost, undo_like_or_dislike_post},
|
||||
activities::post::{dislike::DislikePost, undo_like_or_dislike_post},
|
||||
inbox::new_inbox_routing::Activity,
|
||||
};
|
||||
use activitystreams::activity::kind::UndoType;
|
||||
|
@ -9,7 +9,7 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UndoDislikePost {
|
||||
actor: Url,
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
activities_new::post::{like::LikePost, undo_like_or_dislike_post},
|
||||
activities::post::{like::LikePost, undo_like_or_dislike_post},
|
||||
inbox::new_inbox_routing::Activity,
|
||||
};
|
||||
use activitystreams::activity::kind::UndoType;
|
||||
|
@ -9,7 +9,7 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UndoLikePost {
|
||||
actor: Url,
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
activities_new::{
|
||||
activities::{
|
||||
post::{remove::RemovePost, send_websocket_message},
|
||||
verify_mod_action,
|
||||
},
|
||||
|
@ -15,7 +15,7 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UndoRemovePost {
|
||||
actor: Url,
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{activities_new::post::send_websocket_message, inbox::new_inbox_routing::Activity};
|
||||
use crate::{activities::post::send_websocket_message, inbox::new_inbox_routing::Activity};
|
||||
use activitystreams::{activity::kind::UpdateType, base::BaseExt};
|
||||
use anyhow::Context;
|
||||
use lemmy_api_common::blocking;
|
||||
|
@ -22,7 +22,7 @@ use lemmy_utils::{location_info, LemmyError};
|
|||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdatePost {
|
||||
actor: Url,
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
activities_new::private_message::send_websocket_message,
|
||||
activities::private_message::send_websocket_message,
|
||||
inbox::new_inbox_routing::Activity,
|
||||
};
|
||||
use activitystreams::{activity::kind::CreateType, base::BaseExt};
|
||||
|
@ -10,7 +10,7 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreatePrivateMessage {
|
||||
actor: Url,
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
activities_new::private_message::send_websocket_message,
|
||||
activities::private_message::send_websocket_message,
|
||||
inbox::new_inbox_routing::Activity,
|
||||
};
|
||||
use activitystreams::activity::kind::DeleteType;
|
||||
|
@ -12,12 +12,12 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DeletePrivateMessage {
|
||||
actor: Url,
|
||||
to: Url,
|
||||
pub(in crate::activities_new::private_message) object: Url,
|
||||
pub(in crate::activities::private_message) object: Url,
|
||||
#[serde(rename = "type")]
|
||||
kind: DeleteType,
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
activities_new::private_message::{delete::DeletePrivateMessage, send_websocket_message},
|
||||
activities::private_message::{delete::DeletePrivateMessage, send_websocket_message},
|
||||
inbox::new_inbox_routing::Activity,
|
||||
};
|
||||
use activitystreams::activity::kind::UndoType;
|
||||
|
@ -12,7 +12,7 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UndoDeletePrivateMessage {
|
||||
actor: Url,
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
activities_new::private_message::send_websocket_message,
|
||||
activities::private_message::send_websocket_message,
|
||||
inbox::new_inbox_routing::Activity,
|
||||
};
|
||||
use activitystreams::{activity::kind::UpdateType, base::BaseExt};
|
||||
|
@ -10,7 +10,7 @@ use lemmy_utils::LemmyError;
|
|||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdatePrivateMessage {
|
||||
actor: Url,
|
|
@ -1,57 +0,0 @@
|
|||
use activitystreams::{
|
||||
activity::{ActorAndObjectRef, ActorAndObjectRefExt},
|
||||
base::{AsBase, BaseExt},
|
||||
error::DomainError,
|
||||
};
|
||||
use anyhow::{anyhow, Context};
|
||||
use lemmy_utils::{location_info, LemmyError};
|
||||
use log::debug;
|
||||
use std::fmt::Debug;
|
||||
use url::Url;
|
||||
|
||||
/// Return HTTP 501 for unsupported activities in inbox.
|
||||
pub(crate) fn receive_unhandled_activity<A>(activity: A) -> Result<(), LemmyError>
|
||||
where
|
||||
A: Debug,
|
||||
{
|
||||
debug!("received unhandled activity type: {:?}", activity);
|
||||
Err(anyhow!("Activity not supported").into())
|
||||
}
|
||||
|
||||
/// Ensure that the ID of an incoming activity comes from the same domain as the actor. Optionally
|
||||
/// also checks the ID of the inner object.
|
||||
///
|
||||
/// The reason that this starts with the actor ID is that it was already confirmed as correct by the
|
||||
/// HTTP signature.
|
||||
pub(crate) fn verify_activity_domains_valid<T, Kind>(
|
||||
activity: &T,
|
||||
actor_id: &Url,
|
||||
object_domain_must_match: bool,
|
||||
) -> Result<(), LemmyError>
|
||||
where
|
||||
T: AsBase<Kind> + ActorAndObjectRef,
|
||||
{
|
||||
let expected_domain = actor_id.domain().context(location_info!())?;
|
||||
|
||||
activity.id(expected_domain)?;
|
||||
|
||||
let object_id = match activity.object().to_owned().single_xsd_any_uri() {
|
||||
// object is just an ID
|
||||
Some(id) => id,
|
||||
// object is something like an activity, a comment or a post
|
||||
None => activity
|
||||
.object()
|
||||
.to_owned()
|
||||
.one()
|
||||
.context(location_info!())?
|
||||
.id()
|
||||
.context(location_info!())?
|
||||
.to_owned(),
|
||||
};
|
||||
|
||||
if object_domain_must_match && object_id.domain() != Some(expected_domain) {
|
||||
return Err(DomainError.into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
use anyhow::anyhow;
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_db_queries::ApubObject;
|
||||
use lemmy_db_schema::source::{community::Community, person::Person};
|
||||
use lemmy_db_views_actor::community_view::CommunityView;
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
||||
pub mod comment;
|
||||
pub mod community;
|
||||
pub mod follow;
|
||||
pub mod post;
|
||||
pub mod private_message;
|
||||
|
||||
async fn verify_mod_action(
|
||||
actor_id: Url,
|
||||
activity_cc: Url,
|
||||
context: &LemmyContext,
|
||||
) -> Result<(), LemmyError> {
|
||||
let community = blocking(context.pool(), move |conn| {
|
||||
Community::read_from_apub_id(conn, &activity_cc.into())
|
||||
})
|
||||
.await??;
|
||||
|
||||
if community.local {
|
||||
let actor = blocking(&context.pool(), move |conn| {
|
||||
Person::read_from_apub_id(&conn, &actor_id.clone().into())
|
||||
})
|
||||
.await??;
|
||||
|
||||
// Note: this will also return true for admins in addition to mods, but as we dont know about
|
||||
// remote admins, it doesnt make any difference.
|
||||
let community_id = community.id;
|
||||
let actor_id = actor.id;
|
||||
let is_mod_or_admin = blocking(context.pool(), move |conn| {
|
||||
CommunityView::is_mod_or_admin(conn, actor_id, community_id)
|
||||
})
|
||||
.await?;
|
||||
if !is_mod_or_admin {
|
||||
return Err(anyhow!("Not a mod").into());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
|
@ -1,160 +1,14 @@
|
|||
use crate::inbox::{
|
||||
assert_activity_not_local,
|
||||
get_activity_id,
|
||||
inbox_verify_http_signature,
|
||||
is_activity_already_known,
|
||||
receive_for_community::receive_add_for_community,
|
||||
verify_is_addressed_to_public,
|
||||
};
|
||||
use activitystreams::{activity::ActorAndObject, prelude::*};
|
||||
use crate::inbox::new_inbox_routing::{Activity, SharedInboxActivities};
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use anyhow::{anyhow, Context};
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_apub::{
|
||||
check_community_or_site_ban,
|
||||
get_activity_to_and_cc,
|
||||
insert_activity,
|
||||
ActorType,
|
||||
CommunityType,
|
||||
};
|
||||
use lemmy_db_queries::{source::community::Community_, ApubObject};
|
||||
use lemmy_db_schema::source::{community::Community, person::Person};
|
||||
use lemmy_utils::{location_info, LemmyError};
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use log::info;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// Allowed activities for community inbox.
|
||||
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub enum CommunityValidTypes {
|
||||
Follow, // follow request from a person
|
||||
Undo, // unfollow from a person
|
||||
Create, // create post or comment
|
||||
Update, // update post or comment
|
||||
Like, // upvote post or comment
|
||||
Dislike, // downvote post or comment
|
||||
Delete, // post or comment deleted by creator
|
||||
Remove, // post or comment removed by mod or admin, or mod removed from community
|
||||
Add, // mod added to community
|
||||
Block, // user blocked by community
|
||||
}
|
||||
|
||||
pub type CommunityAcceptedActivities = ActorAndObject<CommunityValidTypes>;
|
||||
|
||||
/// Handler for all incoming receive to community inboxes.
|
||||
pub async fn community_inbox(
|
||||
request: HttpRequest,
|
||||
input: web::Json<CommunityAcceptedActivities>,
|
||||
path: web::Path<String>,
|
||||
context: web::Data<LemmyContext>,
|
||||
_request: HttpRequest,
|
||||
_input: web::Json<Activity<Activity<SharedInboxActivities>>>,
|
||||
_path: web::Path<String>,
|
||||
_context: web::Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let activity = input.into_inner();
|
||||
// First of all check the http signature
|
||||
let request_counter = &mut 0;
|
||||
let actor = inbox_verify_http_signature(&activity, &context, request, request_counter).await?;
|
||||
|
||||
// Do nothing if we received the same activity before
|
||||
let activity_id = get_activity_id(&activity, &actor.actor_id())?;
|
||||
if is_activity_already_known(context.pool(), &activity_id).await? {
|
||||
return Ok(HttpResponse::Ok().finish());
|
||||
}
|
||||
|
||||
// Check if the activity is actually meant for us
|
||||
let path = path.into_inner();
|
||||
let community = blocking(&context.pool(), move |conn| {
|
||||
Community::read_from_name(&conn, &path)
|
||||
})
|
||||
.await??;
|
||||
let to_and_cc = get_activity_to_and_cc(&activity);
|
||||
if !to_and_cc.contains(&&community.actor_id()) {
|
||||
return Err(anyhow!("Activity delivered to wrong community").into());
|
||||
}
|
||||
|
||||
assert_activity_not_local(&activity)?;
|
||||
insert_activity(&activity_id, activity.clone(), false, true, context.pool()).await?;
|
||||
|
||||
community_receive_message(
|
||||
activity.clone(),
|
||||
community.clone(),
|
||||
actor.as_ref(),
|
||||
&context,
|
||||
request_counter,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Receives Follow, Undo/Follow, post actions, comment actions (including votes)
|
||||
pub(crate) async fn community_receive_message(
|
||||
activity: CommunityAcceptedActivities,
|
||||
to_community: Community,
|
||||
actor: &dyn ActorType,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
// Only persons can send activities to the community, so we can get the actor as person
|
||||
// unconditionally.
|
||||
let actor_id = actor.actor_id();
|
||||
let person = blocking(&context.pool(), move |conn| {
|
||||
Person::read_from_apub_id(&conn, &actor_id.into())
|
||||
})
|
||||
.await??;
|
||||
check_community_or_site_ban(&person, to_community.id, context.pool()).await?;
|
||||
|
||||
info!(
|
||||
"Community {} received activity {} from {}",
|
||||
to_community.name,
|
||||
&activity
|
||||
.id_unchecked()
|
||||
.context(location_info!())?
|
||||
.to_string(),
|
||||
&person.actor_id().to_string()
|
||||
);
|
||||
|
||||
let any_base = activity.clone().into_any_base()?;
|
||||
let activity_kind = activity.kind().context(location_info!())?;
|
||||
let do_announce = match activity_kind {
|
||||
CommunityValidTypes::Follow => todo!(),
|
||||
CommunityValidTypes::Undo => todo!(),
|
||||
CommunityValidTypes::Create => todo!(),
|
||||
CommunityValidTypes::Update => todo!(),
|
||||
CommunityValidTypes::Like => todo!(),
|
||||
CommunityValidTypes::Dislike => todo!(),
|
||||
CommunityValidTypes::Delete => todo!(),
|
||||
CommunityValidTypes::Add => {
|
||||
Box::pin(receive_add_for_community(
|
||||
context,
|
||||
any_base.clone(),
|
||||
None,
|
||||
request_counter,
|
||||
))
|
||||
.await?;
|
||||
true
|
||||
}
|
||||
CommunityValidTypes::Remove => todo!(),
|
||||
CommunityValidTypes::Block => todo!(),
|
||||
};
|
||||
|
||||
if do_announce {
|
||||
// Check again that the activity is public, just to be sure
|
||||
verify_is_addressed_to_public(&activity)?;
|
||||
let mut object_actor = activity.object().clone().single_xsd_any_uri();
|
||||
// If activity is something like Undo/Block, we need to access activity.object.object
|
||||
if object_actor.is_none() {
|
||||
object_actor = activity
|
||||
.object()
|
||||
.as_one()
|
||||
.map(|a| ActorAndObject::from_any_base(a.to_owned()).ok())
|
||||
.flatten()
|
||||
.flatten()
|
||||
.map(|a: ActorAndObject<CommunityValidTypes>| a.object().to_owned().single_xsd_any_uri())
|
||||
.flatten();
|
||||
}
|
||||
to_community
|
||||
.send_announce(activity.into_any_base()?, object_actor, context)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(HttpResponse::Ok().finish())
|
||||
todo!()
|
||||
}
|
||||
|
|
|
@ -1,48 +1,29 @@
|
|||
use activitystreams::{
|
||||
activity::ActorAndObjectRefExt,
|
||||
base::{AsBase, BaseExt, Extends},
|
||||
base::{AsBase, Extends},
|
||||
object::AsObject,
|
||||
public,
|
||||
};
|
||||
use actix_web::HttpRequest;
|
||||
use anyhow::{anyhow, Context};
|
||||
use anyhow::Context;
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_apub::{
|
||||
check_is_apub_id_valid,
|
||||
extensions::signatures::verify_signature,
|
||||
fetcher::get_or_fetch_and_upsert_actor,
|
||||
get_activity_to_and_cc,
|
||||
ActorType,
|
||||
};
|
||||
use lemmy_db_queries::{
|
||||
source::{activity::Activity_, community::Community_},
|
||||
ApubObject,
|
||||
DbPool,
|
||||
};
|
||||
use lemmy_db_schema::source::{activity::Activity, community::Community, person::Person};
|
||||
use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
|
||||
use lemmy_db_queries::{source::activity::Activity_, DbPool};
|
||||
use lemmy_db_schema::source::activity::Activity;
|
||||
use lemmy_utils::{location_info, LemmyError};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use serde::Serialize;
|
||||
use std::fmt::Debug;
|
||||
use url::Url;
|
||||
|
||||
pub mod community_inbox;
|
||||
pub mod new_inbox_routing;
|
||||
pub mod person_inbox;
|
||||
pub(crate) mod receive_for_community;
|
||||
pub mod shared_inbox;
|
||||
|
||||
pub(crate) fn get_activity_id<T, Kind>(activity: &T, creator_uri: &Url) -> Result<Url, LemmyError>
|
||||
where
|
||||
T: BaseExt<Kind> + Extends<Kind> + Debug,
|
||||
Kind: Serialize,
|
||||
<T as Extends<Kind>>::Error: From<serde_json::Error> + Send + Sync + 'static,
|
||||
{
|
||||
let creator_domain = creator_uri.host_str().context(location_info!())?;
|
||||
let activity_id = activity.id(creator_domain)?;
|
||||
Ok(activity_id.context(location_info!())?.to_owned())
|
||||
}
|
||||
|
||||
pub(crate) async fn is_activity_already_known(
|
||||
pool: &DbPool,
|
||||
activity_id: &Url,
|
||||
|
@ -58,18 +39,6 @@ pub(crate) async fn is_activity_already_known(
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn verify_is_addressed_to_public<T, Kind>(activity: &T) -> Result<(), LemmyError>
|
||||
where
|
||||
T: AsBase<Kind> + AsObject<Kind> + ActorAndObjectRefExt,
|
||||
{
|
||||
let to_and_cc = get_activity_to_and_cc(activity);
|
||||
if to_and_cc.contains(&public()) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow!("Activity is not addressed to public").into())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn inbox_verify_http_signature<T, Kind>(
|
||||
activity: &T,
|
||||
context: &LemmyContext,
|
||||
|
@ -91,64 +60,3 @@ where
|
|||
verify_signature(&request, actor.as_ref())?;
|
||||
Ok(actor)
|
||||
}
|
||||
|
||||
/// Returns true if `to_and_cc` contains at least one local user.
|
||||
pub(crate) async fn is_addressed_to_local_person(
|
||||
to_and_cc: &[Url],
|
||||
pool: &DbPool,
|
||||
) -> Result<bool, LemmyError> {
|
||||
for url in to_and_cc {
|
||||
let url = url.to_owned();
|
||||
let person = blocking(&pool, move |conn| {
|
||||
Person::read_from_apub_id(&conn, &url.into())
|
||||
})
|
||||
.await?;
|
||||
if let Ok(u) = person {
|
||||
if u.local {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// If `to_and_cc` contains the followers collection of a remote community, returns this community
|
||||
/// (like `https://example.com/c/main/followers`)
|
||||
pub(crate) async fn is_addressed_to_community_followers(
|
||||
to_and_cc: &[Url],
|
||||
pool: &DbPool,
|
||||
) -> Result<Option<Community>, LemmyError> {
|
||||
for url in to_and_cc {
|
||||
let url = url.to_owned().into();
|
||||
let community = blocking(&pool, move |conn| {
|
||||
// ignore errors here, because the current url might not actually be a followers url
|
||||
Community::read_from_followers_url(&conn, &url).ok()
|
||||
})
|
||||
.await?;
|
||||
if let Some(c) = community {
|
||||
if !c.local {
|
||||
return Ok(Some(c));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub(in crate::inbox) fn assert_activity_not_local<T, Kind>(activity: &T) -> Result<(), LemmyError>
|
||||
where
|
||||
T: BaseExt<Kind> + Debug,
|
||||
{
|
||||
let id = activity.id_unchecked().context(location_info!())?;
|
||||
let activity_domain = id.domain().context(location_info!())?;
|
||||
|
||||
if activity_domain == Settings::get().hostname() {
|
||||
return Err(
|
||||
anyhow!(
|
||||
"Error: received activity which was sent by local instance: {:?}",
|
||||
activity
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::activities_new::{
|
||||
use crate::activities::{
|
||||
comment::{
|
||||
create::CreateComment,
|
||||
delete::DeleteComment,
|
||||
|
@ -12,8 +12,11 @@ use crate::activities_new::{
|
|||
update::UpdateComment,
|
||||
},
|
||||
community::{
|
||||
announce::AnnounceActivity,
|
||||
block_user::BlockUserFromCommunity,
|
||||
delete::DeleteCommunity,
|
||||
remove::RemoveCommunity,
|
||||
undo_block_user::UndoBlockUserFromCommunity,
|
||||
undo_delete::UndoDeleteCommunity,
|
||||
undo_remove::UndoRemoveCommunity,
|
||||
update::UpdateCommunity,
|
||||
|
@ -68,7 +71,7 @@ impl<Kind> Activity<Kind> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub enum SharedInboxActivities {
|
||||
FollowCommunity(FollowCommunity),
|
||||
AcceptFollowCommunity(AcceptFollowCommunity),
|
||||
|
@ -97,11 +100,14 @@ pub enum SharedInboxActivities {
|
|||
UndoRemovePost(UndoRemovePost),
|
||||
UndoLikePost(UndoLikePost),
|
||||
UndoDislikePost(UndoDislikePost),
|
||||
AnnounceActivity(AnnounceActivity),
|
||||
UpdateCommunity(UpdateCommunity),
|
||||
DeleteCommunity(DeleteCommunity),
|
||||
RemoveCommunity(RemoveCommunity),
|
||||
UndoDeleteCommunity(UndoDeleteCommunity),
|
||||
UndoRemoveCommunity(UndoRemoveCommunity),
|
||||
BlockUserFromCommunity(BlockUserFromCommunity),
|
||||
UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
|
||||
}
|
||||
|
||||
// todo: can probably get rid of these?
|
||||
|
|
|
@ -1,223 +1,13 @@
|
|||
use crate::{
|
||||
activities::receive::{receive_unhandled_activity, verify_activity_domains_valid},
|
||||
inbox::{
|
||||
is_activity_already_known,
|
||||
is_addressed_to_community_followers,
|
||||
is_addressed_to_local_person,
|
||||
new_inbox_routing::{Activity, SharedInboxActivities},
|
||||
receive_for_community::receive_add_for_community,
|
||||
verify_is_addressed_to_public,
|
||||
},
|
||||
};
|
||||
use activitystreams::{
|
||||
activity::{ActorAndObject, Announce},
|
||||
base::AnyBase,
|
||||
prelude::*,
|
||||
};
|
||||
use crate::inbox::new_inbox_routing::{Activity, SharedInboxActivities};
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use anyhow::{anyhow, Context};
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_apub::{check_is_apub_id_valid, get_activity_to_and_cc, ActorType};
|
||||
use lemmy_apub_lib::{ReceiveActivity, VerifyActivity};
|
||||
use lemmy_db_queries::Followable;
|
||||
use lemmy_db_schema::source::{community::CommunityFollower, person::Person};
|
||||
use lemmy_utils::{location_info, LemmyError};
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use log::info;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
use strum_macros::EnumString;
|
||||
|
||||
/// Allowed activities for person inbox.
|
||||
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub enum PersonValidTypes {
|
||||
Accept, // community accepted our follow request
|
||||
Create, // create private message
|
||||
Update, // edit private message
|
||||
Delete, // private message or community deleted by creator
|
||||
Undo, // private message or community restored
|
||||
Remove, // community removed by admin
|
||||
Announce, // post, comment or vote in community
|
||||
}
|
||||
|
||||
pub type PersonAcceptedActivities = ActorAndObject<PersonValidTypes>;
|
||||
|
||||
/// Handler for all incoming activities to person inboxes.
|
||||
pub async fn person_inbox(
|
||||
_request: HttpRequest,
|
||||
input: web::Json<Activity<SharedInboxActivities>>,
|
||||
_input: web::Json<Activity<Activity<SharedInboxActivities>>>,
|
||||
_path: web::Path<String>,
|
||||
context: web::Data<LemmyContext>,
|
||||
_context: web::Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let activity = input.into_inner();
|
||||
activity.inner.verify(&context).await?;
|
||||
let request_counter = &mut 0;
|
||||
activity.inner.receive(&context, request_counter).await?;
|
||||
todo!()
|
||||
/*
|
||||
// First of all check the http signature
|
||||
let request_counter = &mut 0;
|
||||
let actor = inbox_verify_http_signature(&activity, &context, request, request_counter).await?;
|
||||
|
||||
// Do nothing if we received the same activity before
|
||||
let activity_id = get_activity_id(&activity, &actor.actor_id())?;
|
||||
if is_activity_already_known(context.pool(), &activity_id).await? {
|
||||
return Ok(HttpResponse::Ok().finish());
|
||||
}
|
||||
|
||||
// Check if the activity is actually meant for us
|
||||
let username = path.into_inner();
|
||||
let person = blocking(&context.pool(), move |conn| {
|
||||
Person::find_by_name(&conn, &username)
|
||||
})
|
||||
.await??;
|
||||
let to_and_cc = get_activity_to_and_cc(&activity);
|
||||
// TODO: we should also accept activities that are sent to community followers
|
||||
if !to_and_cc.contains(&&person.actor_id()) {
|
||||
return Err(anyhow!("Activity delivered to wrong person").into());
|
||||
}
|
||||
|
||||
assert_activity_not_local(&activity)?;
|
||||
insert_activity(&activity_id, activity.clone(), false, true, context.pool()).await?;
|
||||
|
||||
person_receive_message(
|
||||
activity.clone(),
|
||||
Some(person.clone()),
|
||||
actor.as_ref(),
|
||||
&context,
|
||||
request_counter,
|
||||
)
|
||||
.await
|
||||
*/
|
||||
}
|
||||
|
||||
/// Receives Accept/Follow, Announce, private messages and community (undo) remove, (undo) delete
|
||||
pub(crate) async fn person_receive_message(
|
||||
activity: PersonAcceptedActivities,
|
||||
_to_person: Option<Person>,
|
||||
actor: &dyn ActorType,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
is_for_person_inbox(context, &activity).await?;
|
||||
|
||||
info!(
|
||||
"User received activity {:?} from {}",
|
||||
&activity
|
||||
.id_unchecked()
|
||||
.context(location_info!())?
|
||||
.to_string(),
|
||||
&actor.actor_id().to_string()
|
||||
);
|
||||
|
||||
let any_base = activity.clone().into_any_base()?;
|
||||
let kind = activity.kind().context(location_info!())?;
|
||||
match kind {
|
||||
PersonValidTypes::Accept => {}
|
||||
PersonValidTypes::Announce => {
|
||||
Box::pin(receive_announce(&context, any_base, actor, request_counter)).await?
|
||||
}
|
||||
PersonValidTypes::Create => {}
|
||||
PersonValidTypes::Update => {}
|
||||
PersonValidTypes::Delete => todo!(),
|
||||
PersonValidTypes::Undo => todo!(),
|
||||
PersonValidTypes::Remove => todo!(),
|
||||
};
|
||||
|
||||
// TODO: would be logical to move websocket notification code here
|
||||
|
||||
Ok(HttpResponse::Ok().finish())
|
||||
}
|
||||
|
||||
/// Returns true if the activity is addressed directly to one or more local persons, or if it is
|
||||
/// addressed to the followers collection of a remote community, and at least one local person follows
|
||||
/// it.
|
||||
async fn is_for_person_inbox(
|
||||
context: &LemmyContext,
|
||||
activity: &PersonAcceptedActivities,
|
||||
) -> Result<(), LemmyError> {
|
||||
let to_and_cc = get_activity_to_and_cc(activity);
|
||||
// Check if it is addressed directly to any local person
|
||||
if is_addressed_to_local_person(&to_and_cc, context.pool()).await? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Check if it is addressed to any followers collection of a remote community, and that the
|
||||
// community has local followers.
|
||||
let community = is_addressed_to_community_followers(&to_and_cc, context.pool()).await?;
|
||||
if let Some(c) = community {
|
||||
let community_id = c.id;
|
||||
let has_local_followers = blocking(&context.pool(), move |conn| {
|
||||
CommunityFollower::has_local_followers(conn, community_id)
|
||||
})
|
||||
.await??;
|
||||
if c.local {
|
||||
return Err(
|
||||
anyhow!("Remote activity cant be addressed to followers of local community").into(),
|
||||
);
|
||||
}
|
||||
if has_local_followers {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
Err(anyhow!("Not addressed for any local person").into())
|
||||
}
|
||||
|
||||
#[derive(EnumString)]
|
||||
enum AnnouncableActivities {
|
||||
Create,
|
||||
Update,
|
||||
Like,
|
||||
Dislike,
|
||||
Delete,
|
||||
Remove,
|
||||
Undo,
|
||||
Add,
|
||||
Block,
|
||||
}
|
||||
|
||||
/// Takes an announce and passes the inner activity to the appropriate handler.
|
||||
pub async fn receive_announce(
|
||||
context: &LemmyContext,
|
||||
activity: AnyBase,
|
||||
actor: &dyn ActorType,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let announce = Announce::from_any_base(activity)?.context(location_info!())?;
|
||||
verify_activity_domains_valid(&announce, &actor.actor_id(), false)?;
|
||||
verify_is_addressed_to_public(&announce)?;
|
||||
|
||||
let kind = announce
|
||||
.object()
|
||||
.as_single_kind_str()
|
||||
.and_then(|s| s.parse().ok());
|
||||
let inner_activity = announce
|
||||
.object()
|
||||
.to_owned()
|
||||
.one()
|
||||
.context(location_info!())?;
|
||||
|
||||
let inner_id = inner_activity.id().context(location_info!())?.to_owned();
|
||||
check_is_apub_id_valid(&inner_id, false)?;
|
||||
if is_activity_already_known(context.pool(), &inner_id).await? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
use AnnouncableActivities::*;
|
||||
match kind {
|
||||
Some(Create) => todo!(),
|
||||
Some(Update) => todo!(),
|
||||
Some(Like) => todo!(),
|
||||
Some(Dislike) => todo!(),
|
||||
Some(Delete) => todo!(),
|
||||
Some(Remove) => todo!(),
|
||||
Some(Undo) => todo!(),
|
||||
Some(Add) => {
|
||||
receive_add_for_community(context, inner_activity, Some(announce), request_counter).await
|
||||
}
|
||||
Some(Block) => todo!(),
|
||||
_ => receive_unhandled_activity(inner_activity),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,214 +0,0 @@
|
|||
use crate::{
|
||||
activities::receive::verify_activity_domains_valid,
|
||||
inbox::verify_is_addressed_to_public,
|
||||
};
|
||||
use activitystreams::{
|
||||
activity::{ActorAndObjectRef, Add, Announce, OptTargetRef},
|
||||
base::AnyBase,
|
||||
object::AsObject,
|
||||
prelude::*,
|
||||
};
|
||||
use anyhow::{anyhow, Context};
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_apub::{
|
||||
fetcher::person::get_or_fetch_and_upsert_person,
|
||||
generate_moderators_url,
|
||||
CommunityType,
|
||||
};
|
||||
use lemmy_db_queries::{source::community::CommunityModerator_, ApubObject, Joinable};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
community::{Community, CommunityModerator, CommunityModeratorForm},
|
||||
person::Person,
|
||||
},
|
||||
DbUrl,
|
||||
};
|
||||
use lemmy_db_views_actor::community_view::CommunityView;
|
||||
use lemmy_utils::{location_info, LemmyError};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use strum_macros::EnumString;
|
||||
|
||||
#[derive(EnumString)]
|
||||
enum PageOrNote {
|
||||
Page,
|
||||
Note,
|
||||
}
|
||||
|
||||
#[derive(EnumString)]
|
||||
enum ObjectTypes {
|
||||
Page,
|
||||
Note,
|
||||
Group,
|
||||
Person,
|
||||
}
|
||||
|
||||
#[derive(EnumString)]
|
||||
enum UndoableActivities {
|
||||
Delete,
|
||||
Remove,
|
||||
Like,
|
||||
Dislike,
|
||||
Block,
|
||||
}
|
||||
|
||||
/// Add a new mod to the community (can only be done by an existing mod).
|
||||
pub(in crate::inbox) async fn receive_add_for_community(
|
||||
context: &LemmyContext,
|
||||
add_any_base: AnyBase,
|
||||
announce: Option<Announce>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let add = Add::from_any_base(add_any_base.to_owned())?.context(location_info!())?;
|
||||
let community = extract_community_from_cc(&add, context).await?;
|
||||
|
||||
verify_mod_activity(&add, announce, &community, context).await?;
|
||||
verify_is_addressed_to_public(&add)?;
|
||||
verify_add_remove_moderator_target(&add, &community)?;
|
||||
|
||||
let new_mod = add
|
||||
.object()
|
||||
.as_single_xsd_any_uri()
|
||||
.context(location_info!())?;
|
||||
let new_mod = get_or_fetch_and_upsert_person(&new_mod, context, request_counter).await?;
|
||||
|
||||
// If we had to refetch the community while parsing the activity, then the new mod has already
|
||||
// been added. Skip it here as it would result in a duplicate key error.
|
||||
let new_mod_id = new_mod.id;
|
||||
let moderated_communities = blocking(context.pool(), move |conn| {
|
||||
CommunityModerator::get_person_moderated_communities(conn, new_mod_id)
|
||||
})
|
||||
.await??;
|
||||
if !moderated_communities.contains(&community.id) {
|
||||
let form = CommunityModeratorForm {
|
||||
community_id: community.id,
|
||||
person_id: new_mod.id,
|
||||
};
|
||||
blocking(context.pool(), move |conn| {
|
||||
CommunityModerator::join(conn, &form)
|
||||
})
|
||||
.await??;
|
||||
}
|
||||
if community.local {
|
||||
community
|
||||
.send_announce(
|
||||
add_any_base,
|
||||
add.object().clone().single_xsd_any_uri(),
|
||||
context,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
// TODO: send websocket notification about added mod
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Searches the activity's cc field for a Community ID, and returns the community.
|
||||
async fn extract_community_from_cc<T, Kind>(
|
||||
activity: &T,
|
||||
context: &LemmyContext,
|
||||
) -> Result<Community, LemmyError>
|
||||
where
|
||||
T: AsObject<Kind>,
|
||||
{
|
||||
let cc = activity
|
||||
.cc()
|
||||
.map(|c| c.as_many())
|
||||
.flatten()
|
||||
.context(location_info!())?;
|
||||
let community_id = cc
|
||||
.first()
|
||||
.map(|c| c.as_xsd_any_uri())
|
||||
.flatten()
|
||||
.context(location_info!())?;
|
||||
let community_id: DbUrl = community_id.to_owned().into();
|
||||
let community = blocking(&context.pool(), move |conn| {
|
||||
Community::read_from_apub_id(&conn, &community_id)
|
||||
})
|
||||
.await??;
|
||||
Ok(community)
|
||||
}
|
||||
|
||||
/// Checks that a moderation activity was sent by a user who is listed as mod for the community.
|
||||
/// This is only used in the case of remote mods, as local mod actions don't go through the
|
||||
/// community inbox.
|
||||
///
|
||||
/// This method should only be used for activities received by the community, not for activities
|
||||
/// used by community followers.
|
||||
pub(crate) async fn verify_actor_is_community_mod<T, Kind>(
|
||||
activity: &T,
|
||||
community: &Community,
|
||||
context: &LemmyContext,
|
||||
) -> Result<(), LemmyError>
|
||||
where
|
||||
T: ActorAndObjectRef + BaseExt<Kind>,
|
||||
{
|
||||
let actor = activity
|
||||
.actor()?
|
||||
.as_single_xsd_any_uri()
|
||||
.context(location_info!())?
|
||||
.to_owned();
|
||||
let actor = blocking(&context.pool(), move |conn| {
|
||||
Person::read_from_apub_id(&conn, &actor.into())
|
||||
})
|
||||
.await??;
|
||||
|
||||
// Note: this will also return true for admins in addition to mods, but as we dont know about
|
||||
// remote admins, it doesnt make any difference.
|
||||
let community_id = community.id;
|
||||
let actor_id = actor.id;
|
||||
let is_mod_or_admin = blocking(context.pool(), move |conn| {
|
||||
CommunityView::is_mod_or_admin(conn, actor_id, community_id)
|
||||
})
|
||||
.await?;
|
||||
if !is_mod_or_admin {
|
||||
return Err(anyhow!("Not a mod").into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This method behaves differently, depending if it is called via community inbox (activity
|
||||
/// received by community from a remote user), or via user inbox (activity received by user from
|
||||
/// community). We distinguish the cases by checking if the activity is wrapper in an announce
|
||||
/// (only true when sent from user to community).
|
||||
///
|
||||
/// In the first case, we check that the actor is listed as community mod. In the second case, we
|
||||
/// only check that the announce comes from the same domain as the activity. We trust the
|
||||
/// community's instance to have validated the inner activity correctly. We can't do this validation
|
||||
/// here, because we don't know who the instance admins are. Plus this allows for compatibility with
|
||||
/// software that uses different rules for mod actions.
|
||||
pub(crate) async fn verify_mod_activity<T, Kind>(
|
||||
mod_action: &T,
|
||||
announce: Option<Announce>,
|
||||
community: &Community,
|
||||
context: &LemmyContext,
|
||||
) -> Result<(), LemmyError>
|
||||
where
|
||||
T: ActorAndObjectRef + BaseExt<Kind>,
|
||||
{
|
||||
match announce {
|
||||
None => verify_actor_is_community_mod(mod_action, community, context).await?,
|
||||
Some(a) => verify_activity_domains_valid(&a, &community.actor_id.to_owned().into(), false)?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// For Add/Remove community moderator activities, check that the target field actually contains
|
||||
/// /c/community/moderators. Any different values are unsupported.
|
||||
fn verify_add_remove_moderator_target<T, Kind>(
|
||||
activity: &T,
|
||||
community: &Community,
|
||||
) -> Result<(), LemmyError>
|
||||
where
|
||||
T: ActorAndObjectRef + BaseExt<Kind> + OptTargetRef,
|
||||
{
|
||||
let target = activity
|
||||
.target()
|
||||
.map(|t| t.as_single_xsd_any_uri())
|
||||
.flatten()
|
||||
.context(location_info!())?;
|
||||
if target != &generate_moderators_url(&community.actor_id)?.into_inner() {
|
||||
return Err(anyhow!("Unkown target url").into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
|
@ -1,151 +1,17 @@
|
|||
use crate::inbox::{
|
||||
assert_activity_not_local,
|
||||
community_inbox::{community_receive_message, CommunityAcceptedActivities},
|
||||
get_activity_id,
|
||||
inbox_verify_http_signature,
|
||||
is_activity_already_known,
|
||||
is_addressed_to_community_followers,
|
||||
is_addressed_to_local_person,
|
||||
person_inbox::{person_receive_message, PersonAcceptedActivities},
|
||||
};
|
||||
use activitystreams::{activity::ActorAndObject, prelude::*};
|
||||
use crate::inbox::new_inbox_routing::{Activity, SharedInboxActivities};
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use anyhow::Context;
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_apub::{get_activity_to_and_cc, insert_activity};
|
||||
use lemmy_db_queries::{ApubObject, DbPool};
|
||||
use lemmy_db_schema::source::community::Community;
|
||||
use lemmy_utils::{location_info, LemmyError};
|
||||
use lemmy_apub_lib::{ReceiveActivity, VerifyActivity};
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
use url::Url;
|
||||
|
||||
/// Allowed activity types for shared inbox.
|
||||
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub enum ValidTypes {
|
||||
Create,
|
||||
Update,
|
||||
Like,
|
||||
Dislike,
|
||||
Delete,
|
||||
Undo,
|
||||
Remove,
|
||||
Announce,
|
||||
Add,
|
||||
Block,
|
||||
}
|
||||
|
||||
// TODO: this isnt entirely correct, cause some of these receive are not ActorAndObject,
|
||||
// but it still works due to the anybase conversion
|
||||
pub type AcceptedActivities = ActorAndObject<ValidTypes>;
|
||||
|
||||
/// Handler for all incoming requests to shared inbox.
|
||||
pub async fn shared_inbox(
|
||||
request: HttpRequest,
|
||||
input: web::Json<AcceptedActivities>,
|
||||
_request: HttpRequest,
|
||||
input: web::Json<Activity<SharedInboxActivities>>,
|
||||
context: web::Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let activity = input.into_inner();
|
||||
// First of all check the http signature
|
||||
activity.inner.verify(&context).await?;
|
||||
let request_counter = &mut 0;
|
||||
let actor = inbox_verify_http_signature(&activity, &context, request, request_counter).await?;
|
||||
|
||||
// Do nothing if we received the same activity before
|
||||
let actor_id = actor.actor_id();
|
||||
let activity_id = get_activity_id(&activity, &actor_id)?;
|
||||
if is_activity_already_known(context.pool(), &activity_id).await? {
|
||||
return Ok(HttpResponse::Ok().finish());
|
||||
}
|
||||
|
||||
assert_activity_not_local(&activity)?;
|
||||
// Log the activity, so we avoid receiving and parsing it twice. Note that this could still happen
|
||||
// if we receive the same activity twice in very quick succession.
|
||||
insert_activity(&activity_id, activity.clone(), false, true, context.pool()).await?;
|
||||
|
||||
let activity_any_base = activity.clone().into_any_base()?;
|
||||
let mut res: Option<HttpResponse> = None;
|
||||
let to_and_cc = get_activity_to_and_cc(&activity);
|
||||
// Handle community first, so in case the sender is banned by the community, it will error out.
|
||||
// If we handled the person receive first, the activity would be inserted to the database before the
|
||||
// community could check for bans.
|
||||
// Note that an activity can be addressed to a community and to a person (or multiple persons) at the
|
||||
// same time. In this case we still only handle it once, to avoid duplicate websocket
|
||||
// notifications.
|
||||
let community = extract_local_community_from_destinations(&to_and_cc, context.pool()).await?;
|
||||
if let Some(community) = community {
|
||||
let community_activity = CommunityAcceptedActivities::from_any_base(activity_any_base.clone())?
|
||||
.context(location_info!())?;
|
||||
res = Some(
|
||||
Box::pin(community_receive_message(
|
||||
community_activity,
|
||||
community,
|
||||
actor.as_ref(),
|
||||
&context,
|
||||
request_counter,
|
||||
))
|
||||
.await?,
|
||||
);
|
||||
} else if is_addressed_to_local_person(&to_and_cc, context.pool()).await? {
|
||||
let person_activity = PersonAcceptedActivities::from_any_base(activity_any_base.clone())?
|
||||
.context(location_info!())?;
|
||||
// `to_person` is only used for follow activities (which we dont receive here), so no need to pass
|
||||
// it in
|
||||
Box::pin(person_receive_message(
|
||||
person_activity,
|
||||
None,
|
||||
actor.as_ref(),
|
||||
&context,
|
||||
request_counter,
|
||||
))
|
||||
.await?;
|
||||
} else if is_addressed_to_community_followers(&to_and_cc, context.pool())
|
||||
.await?
|
||||
.is_some()
|
||||
{
|
||||
let person_activity = PersonAcceptedActivities::from_any_base(activity_any_base.clone())?
|
||||
.context(location_info!())?;
|
||||
res = Some(
|
||||
Box::pin(person_receive_message(
|
||||
person_activity,
|
||||
None,
|
||||
actor.as_ref(),
|
||||
&context,
|
||||
request_counter,
|
||||
))
|
||||
.await?,
|
||||
);
|
||||
}
|
||||
|
||||
// If none of those, throw an error
|
||||
if let Some(r) = res {
|
||||
Ok(r)
|
||||
} else {
|
||||
Ok(HttpResponse::NotImplemented().finish())
|
||||
}
|
||||
}
|
||||
|
||||
/// If `to_and_cc` contains the ID of a local community, return that community, otherwise return
|
||||
/// None.
|
||||
///
|
||||
/// This doesnt handle the case where an activity is addressed to multiple communities (because
|
||||
/// Lemmy doesnt generate such activities).
|
||||
async fn extract_local_community_from_destinations(
|
||||
to_and_cc: &[Url],
|
||||
pool: &DbPool,
|
||||
) -> Result<Option<Community>, LemmyError> {
|
||||
for url in to_and_cc {
|
||||
let url = url.to_owned();
|
||||
let community = blocking(&pool, move |conn| {
|
||||
Community::read_from_apub_id(&conn, &url.into())
|
||||
})
|
||||
.await?;
|
||||
if let Ok(c) = community {
|
||||
if c.local {
|
||||
return Ok(Some(c));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
activity.inner.receive(&context, request_counter).await?;
|
||||
todo!()
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
mod activities;
|
||||
pub mod activities_new;
|
||||
pub mod activities;
|
||||
mod http;
|
||||
mod inbox;
|
||||
pub mod routes;
|
||||
|
|
Loading…
Reference in a new issue