mirror of
https://github.com/LemmyNet/lemmy.git
synced 2025-01-08 11:11:37 +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::{
|
use crate::{
|
||||||
activities_new::comment::{get_notif_recipients, send_websocket_message},
|
activities::comment::{get_notif_recipients, send_websocket_message},
|
||||||
inbox::new_inbox_routing::Activity,
|
inbox::new_inbox_routing::Activity,
|
||||||
};
|
};
|
||||||
use activitystreams::{activity::kind::CreateType, base::BaseExt};
|
use activitystreams::{activity::kind::CreateType, base::BaseExt};
|
||||||
|
@ -10,7 +10,7 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CreateComment {
|
pub struct CreateComment {
|
||||||
actor: Url,
|
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 activitystreams::activity::kind::DeleteType;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub::{check_is_apub_id_valid, fetcher::objects::get_or_fetch_and_insert_comment};
|
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 lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct DeleteComment {
|
pub struct DeleteComment {
|
||||||
pub(in crate::activities_new::comment) actor: Url,
|
pub(in crate::activities::comment) actor: Url,
|
||||||
to: PublicUrl,
|
to: PublicUrl,
|
||||||
pub(in crate::activities_new::comment) object: Url,
|
pub(in crate::activities::comment) object: Url,
|
||||||
cc: [Url; 1],
|
cc: [Url; 1],
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: DeleteType,
|
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 activitystreams::activity::kind::DislikeType;
|
||||||
use lemmy_apub::check_is_apub_id_valid;
|
use lemmy_apub::check_is_apub_id_valid;
|
||||||
use lemmy_apub_lib::{verify_domains_match, PublicUrl, ReceiveActivity, VerifyActivity};
|
use lemmy_apub_lib::{verify_domains_match, PublicUrl, ReceiveActivity, VerifyActivity};
|
||||||
|
@ -6,12 +6,12 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct DislikeComment {
|
pub struct DislikeComment {
|
||||||
actor: Url,
|
actor: Url,
|
||||||
to: PublicUrl,
|
to: PublicUrl,
|
||||||
pub(in crate::activities_new::comment) object: Url,
|
pub(in crate::activities::comment) object: Url,
|
||||||
cc: [Url; 1],
|
cc: [Url; 1],
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: DislikeType,
|
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 activitystreams::activity::kind::LikeType;
|
||||||
use lemmy_apub::check_is_apub_id_valid;
|
use lemmy_apub::check_is_apub_id_valid;
|
||||||
use lemmy_apub_lib::{verify_domains_match, PublicUrl, ReceiveActivity, VerifyActivity};
|
use lemmy_apub_lib::{verify_domains_match, PublicUrl, ReceiveActivity, VerifyActivity};
|
||||||
|
@ -6,12 +6,12 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct LikeComment {
|
pub struct LikeComment {
|
||||||
actor: Url,
|
actor: Url,
|
||||||
to: PublicUrl,
|
to: PublicUrl,
|
||||||
pub(in crate::activities_new::comment) object: Url,
|
pub(in crate::activities::comment) object: Url,
|
||||||
cc: [Url; 1],
|
cc: [Url; 1],
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: LikeType,
|
kind: LikeType,
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities_new::{comment::send_websocket_message, verify_mod_action},
|
activities::{comment::send_websocket_message, verify_mod_action},
|
||||||
inbox::new_inbox_routing::Activity,
|
inbox::new_inbox_routing::Activity,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::RemoveType;
|
use activitystreams::activity::kind::RemoveType;
|
||||||
|
@ -12,12 +12,12 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct RemoveComment {
|
pub struct RemoveComment {
|
||||||
actor: Url,
|
actor: Url,
|
||||||
to: PublicUrl,
|
to: PublicUrl,
|
||||||
pub(in crate::activities_new::comment) object: Url,
|
pub(in crate::activities::comment) object: Url,
|
||||||
cc: [Url; 1],
|
cc: [Url; 1],
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: RemoveType,
|
kind: RemoveType,
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities_new::comment::{delete::DeleteComment, send_websocket_message},
|
activities::comment::{delete::DeleteComment, send_websocket_message},
|
||||||
inbox::new_inbox_routing::Activity,
|
inbox::new_inbox_routing::Activity,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::UndoType;
|
use activitystreams::activity::kind::UndoType;
|
||||||
|
@ -12,7 +12,7 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoDeleteComment {
|
pub struct UndoDeleteComment {
|
||||||
actor: Url,
|
actor: Url,
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
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,
|
inbox::new_inbox_routing::Activity,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::UndoType;
|
use activitystreams::activity::kind::UndoType;
|
||||||
|
@ -9,7 +9,7 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoDislikeComment {
|
pub struct UndoDislikeComment {
|
||||||
actor: Url,
|
actor: Url,
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
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,
|
inbox::new_inbox_routing::Activity,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::UndoType;
|
use activitystreams::activity::kind::UndoType;
|
||||||
|
@ -9,7 +9,7 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoLikeComment {
|
pub struct UndoLikeComment {
|
||||||
actor: Url,
|
actor: Url,
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities_new::{
|
activities::{
|
||||||
comment::{remove::RemoveComment, send_websocket_message},
|
comment::{remove::RemoveComment, send_websocket_message},
|
||||||
verify_mod_action,
|
verify_mod_action,
|
||||||
},
|
},
|
||||||
|
@ -15,7 +15,7 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoRemoveComment {
|
pub struct UndoRemoveComment {
|
||||||
actor: Url,
|
actor: Url,
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities_new::comment::{get_notif_recipients, send_websocket_message},
|
activities::comment::{get_notif_recipients, send_websocket_message},
|
||||||
inbox::new_inbox_routing::Activity,
|
inbox::new_inbox_routing::Activity,
|
||||||
};
|
};
|
||||||
use activitystreams::{activity::kind::UpdateType, base::BaseExt};
|
use activitystreams::{activity::kind::UpdateType, base::BaseExt};
|
||||||
|
@ -10,7 +10,7 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UpdateComment {
|
pub struct UpdateComment {
|
||||||
actor: Url,
|
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 activitystreams::activity::kind::BlockType;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub::{
|
use lemmy_apub::{
|
||||||
|
@ -17,12 +17,12 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct BlockUserFromCommunity {
|
pub struct BlockUserFromCommunity {
|
||||||
actor: Url,
|
actor: Url,
|
||||||
to: PublicUrl,
|
to: PublicUrl,
|
||||||
pub(in crate::activities_new::community) object: Url,
|
pub(in crate::activities::community) object: Url,
|
||||||
cc: [Url; 1],
|
cc: [Url; 1],
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: BlockType,
|
kind: BlockType,
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
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,
|
inbox::new_inbox_routing::Activity,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::DeleteType;
|
use activitystreams::activity::kind::DeleteType;
|
||||||
|
@ -20,12 +20,12 @@ use url::Url;
|
||||||
// We have two possibilities which need to be handled:
|
// We have two possibilities which need to be handled:
|
||||||
// 1. actor is remote mod, community id in object
|
// 1. actor is remote mod, community id in object
|
||||||
// 2. actor is community, cc is followers collection
|
// 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")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct DeleteCommunity {
|
pub struct DeleteCommunity {
|
||||||
actor: Url,
|
actor: Url,
|
||||||
to: PublicUrl,
|
to: PublicUrl,
|
||||||
pub(in crate::activities_new::community) object: Url,
|
pub(in crate::activities::community) object: Url,
|
||||||
cc: [Url; 1],
|
cc: [Url; 1],
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: DeleteType,
|
kind: DeleteType,
|
|
@ -1,5 +1,6 @@
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use lemmy_api_common::{blocking, community::CommunityResponse};
|
use lemmy_api_common::{blocking, community::CommunityResponse};
|
||||||
|
use lemmy_apub::generate_moderators_url;
|
||||||
use lemmy_db_queries::ApubObject;
|
use lemmy_db_queries::ApubObject;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{community::Community, person::Person},
|
source::{community::Community, person::Person},
|
||||||
|
@ -10,9 +11,12 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext};
|
use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
pub mod add_mod;
|
||||||
|
pub mod announce;
|
||||||
pub mod block_user;
|
pub mod block_user;
|
||||||
pub mod delete;
|
pub mod delete;
|
||||||
pub mod remove;
|
pub mod remove;
|
||||||
|
pub mod remove_mod;
|
||||||
pub mod undo_block_user;
|
pub mod undo_block_user;
|
||||||
pub mod undo_delete;
|
pub mod undo_delete;
|
||||||
pub mod undo_remove;
|
pub mod undo_remove;
|
||||||
|
@ -63,3 +67,12 @@ async fn verify_is_community_mod(
|
||||||
}
|
}
|
||||||
Ok(())
|
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::{
|
use crate::{activities::community::send_websocket_message, inbox::new_inbox_routing::Activity};
|
||||||
activities_new::community::send_websocket_message,
|
|
||||||
inbox::new_inbox_routing::Activity,
|
|
||||||
};
|
|
||||||
use activitystreams::activity::kind::RemoveType;
|
use activitystreams::activity::kind::RemoveType;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub::check_is_apub_id_valid;
|
use lemmy_apub::check_is_apub_id_valid;
|
||||||
|
@ -12,12 +9,12 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct RemoveCommunity {
|
pub struct RemoveCommunity {
|
||||||
actor: Url,
|
actor: Url,
|
||||||
to: PublicUrl,
|
to: PublicUrl,
|
||||||
pub(in crate::activities_new::community) object: Url,
|
pub(in crate::activities::community) object: Url,
|
||||||
cc: [Url; 1],
|
cc: [Url; 1],
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: RemoveType,
|
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::{
|
use crate::{
|
||||||
activities_new::{community::block_user::BlockUserFromCommunity, verify_mod_action},
|
activities::{community::block_user::BlockUserFromCommunity, verify_mod_action},
|
||||||
inbox::new_inbox_routing::Activity,
|
inbox::new_inbox_routing::Activity,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::BlockType;
|
use activitystreams::activity::kind::BlockType;
|
||||||
|
@ -15,7 +15,7 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoBlockUserFromCommunity {
|
pub struct UndoBlockUserFromCommunity {
|
||||||
actor: Url,
|
actor: Url,
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities_new::community::{
|
activities::community::{
|
||||||
delete::DeleteCommunity,
|
delete::DeleteCommunity,
|
||||||
send_websocket_message,
|
send_websocket_message,
|
||||||
verify_is_community_mod,
|
verify_is_community_mod,
|
||||||
|
@ -24,7 +24,7 @@ use url::Url;
|
||||||
// We have two possibilities which need to be handled:
|
// We have two possibilities which need to be handled:
|
||||||
// 1. actor is remote mod, community id in object
|
// 1. actor is remote mod, community id in object
|
||||||
// 2. actor is community, cc is followers collection
|
// 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")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoDeleteCommunity {
|
pub struct UndoDeleteCommunity {
|
||||||
actor: Url,
|
actor: Url,
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities_new::community::{remove::RemoveCommunity, send_websocket_message},
|
activities::community::{remove::RemoveCommunity, send_websocket_message},
|
||||||
inbox::new_inbox_routing::Activity,
|
inbox::new_inbox_routing::Activity,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::RemoveType;
|
use activitystreams::activity::kind::RemoveType;
|
||||||
|
@ -12,7 +12,7 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoRemoveCommunity {
|
pub struct UndoRemoveCommunity {
|
||||||
actor: Url,
|
actor: Url,
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
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,
|
inbox::new_inbox_routing::Activity,
|
||||||
};
|
};
|
||||||
use activitystreams::{activity::kind::UpdateType, base::BaseExt};
|
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
|
/// This activity is received from a remote community mod, and updates the description or other
|
||||||
/// fields of a local community.
|
/// fields of a local community.
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UpdateCommunity {
|
pub struct UpdateCommunity {
|
||||||
actor: Url,
|
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 activitystreams::activity::kind::AcceptType;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub::{
|
use lemmy_apub::{
|
||||||
|
@ -12,7 +12,7 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct AcceptFollowCommunity {
|
pub struct AcceptFollowCommunity {
|
||||||
actor: Url,
|
actor: Url,
|
|
@ -17,12 +17,12 @@ use lemmy_utils::{location_info, LemmyError};
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct FollowCommunity {
|
pub struct FollowCommunity {
|
||||||
actor: Url,
|
actor: Url,
|
||||||
to: Url,
|
to: Url,
|
||||||
pub(in crate::activities_new::follow) object: Url,
|
pub(in crate::activities::follow) object: Url,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: FollowType,
|
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 activitystreams::activity::kind::UndoType;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub::{
|
use lemmy_apub::{
|
||||||
|
@ -12,7 +12,7 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoFollowCommunity {
|
pub struct UndoFollowCommunity {
|
||||||
actor: Url,
|
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 activitystreams::{activity::kind::CreateType, base::BaseExt};
|
||||||
use lemmy_apub::{
|
use lemmy_apub::{
|
||||||
check_is_apub_id_valid,
|
check_is_apub_id_valid,
|
||||||
|
@ -13,7 +13,7 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CreatePost {
|
pub struct CreatePost {
|
||||||
actor: Url,
|
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 activitystreams::activity::kind::DeleteType;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub::{check_is_apub_id_valid, fetcher::objects::get_or_fetch_and_insert_post};
|
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 lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct DeletePost {
|
pub struct DeletePost {
|
||||||
pub(in crate::activities_new::post) actor: Url,
|
pub(in crate::activities::post) actor: Url,
|
||||||
to: PublicUrl,
|
to: PublicUrl,
|
||||||
pub(in crate::activities_new::post) object: Url,
|
pub(in crate::activities::post) object: Url,
|
||||||
cc: [Url; 1],
|
cc: [Url; 1],
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: DeleteType,
|
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 activitystreams::activity::kind::DislikeType;
|
||||||
use lemmy_apub::check_is_apub_id_valid;
|
use lemmy_apub::check_is_apub_id_valid;
|
||||||
use lemmy_apub_lib::{verify_domains_match, PublicUrl, ReceiveActivity, VerifyActivity};
|
use lemmy_apub_lib::{verify_domains_match, PublicUrl, ReceiveActivity, VerifyActivity};
|
||||||
|
@ -6,12 +6,12 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct DislikePost {
|
pub struct DislikePost {
|
||||||
actor: Url,
|
actor: Url,
|
||||||
to: PublicUrl,
|
to: PublicUrl,
|
||||||
pub(in crate::activities_new::post) object: Url,
|
pub(in crate::activities::post) object: Url,
|
||||||
cc: [Url; 1],
|
cc: [Url; 1],
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: DislikeType,
|
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 activitystreams::activity::kind::LikeType;
|
||||||
use lemmy_apub::check_is_apub_id_valid;
|
use lemmy_apub::check_is_apub_id_valid;
|
||||||
use lemmy_apub_lib::{verify_domains_match, PublicUrl, ReceiveActivity, VerifyActivity};
|
use lemmy_apub_lib::{verify_domains_match, PublicUrl, ReceiveActivity, VerifyActivity};
|
||||||
|
@ -6,12 +6,12 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct LikePost {
|
pub struct LikePost {
|
||||||
actor: Url,
|
actor: Url,
|
||||||
to: PublicUrl,
|
to: PublicUrl,
|
||||||
pub(in crate::activities_new::post) object: Url,
|
pub(in crate::activities::post) object: Url,
|
||||||
cc: [Url; 1],
|
cc: [Url; 1],
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: LikeType,
|
kind: LikeType,
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities_new::{post::send_websocket_message, verify_mod_action},
|
activities::{post::send_websocket_message, verify_mod_action},
|
||||||
inbox::new_inbox_routing::Activity,
|
inbox::new_inbox_routing::Activity,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::RemoveType;
|
use activitystreams::activity::kind::RemoveType;
|
||||||
|
@ -12,12 +12,12 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct RemovePost {
|
pub struct RemovePost {
|
||||||
actor: Url,
|
actor: Url,
|
||||||
to: PublicUrl,
|
to: PublicUrl,
|
||||||
pub(in crate::activities_new::post) object: Url,
|
pub(in crate::activities::post) object: Url,
|
||||||
cc: [Url; 1],
|
cc: [Url; 1],
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: RemoveType,
|
kind: RemoveType,
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities_new::post::{delete::DeletePost, send_websocket_message},
|
activities::post::{delete::DeletePost, send_websocket_message},
|
||||||
inbox::new_inbox_routing::Activity,
|
inbox::new_inbox_routing::Activity,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::UndoType;
|
use activitystreams::activity::kind::UndoType;
|
||||||
|
@ -12,7 +12,7 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoDeletePost {
|
pub struct UndoDeletePost {
|
||||||
actor: Url,
|
actor: Url,
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
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,
|
inbox::new_inbox_routing::Activity,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::UndoType;
|
use activitystreams::activity::kind::UndoType;
|
||||||
|
@ -9,7 +9,7 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoDislikePost {
|
pub struct UndoDislikePost {
|
||||||
actor: Url,
|
actor: Url,
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
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,
|
inbox::new_inbox_routing::Activity,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::UndoType;
|
use activitystreams::activity::kind::UndoType;
|
||||||
|
@ -9,7 +9,7 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoLikePost {
|
pub struct UndoLikePost {
|
||||||
actor: Url,
|
actor: Url,
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities_new::{
|
activities::{
|
||||||
post::{remove::RemovePost, send_websocket_message},
|
post::{remove::RemovePost, send_websocket_message},
|
||||||
verify_mod_action,
|
verify_mod_action,
|
||||||
},
|
},
|
||||||
|
@ -15,7 +15,7 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoRemovePost {
|
pub struct UndoRemovePost {
|
||||||
actor: Url,
|
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 activitystreams::{activity::kind::UpdateType, base::BaseExt};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
|
@ -22,7 +22,7 @@ use lemmy_utils::{location_info, LemmyError};
|
||||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UpdatePost {
|
pub struct UpdatePost {
|
||||||
actor: Url,
|
actor: Url,
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities_new::private_message::send_websocket_message,
|
activities::private_message::send_websocket_message,
|
||||||
inbox::new_inbox_routing::Activity,
|
inbox::new_inbox_routing::Activity,
|
||||||
};
|
};
|
||||||
use activitystreams::{activity::kind::CreateType, base::BaseExt};
|
use activitystreams::{activity::kind::CreateType, base::BaseExt};
|
||||||
|
@ -10,7 +10,7 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CreatePrivateMessage {
|
pub struct CreatePrivateMessage {
|
||||||
actor: Url,
|
actor: Url,
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities_new::private_message::send_websocket_message,
|
activities::private_message::send_websocket_message,
|
||||||
inbox::new_inbox_routing::Activity,
|
inbox::new_inbox_routing::Activity,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::DeleteType;
|
use activitystreams::activity::kind::DeleteType;
|
||||||
|
@ -12,12 +12,12 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct DeletePrivateMessage {
|
pub struct DeletePrivateMessage {
|
||||||
actor: Url,
|
actor: Url,
|
||||||
to: Url,
|
to: Url,
|
||||||
pub(in crate::activities_new::private_message) object: Url,
|
pub(in crate::activities::private_message) object: Url,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: DeleteType,
|
kind: DeleteType,
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities_new::private_message::{delete::DeletePrivateMessage, send_websocket_message},
|
activities::private_message::{delete::DeletePrivateMessage, send_websocket_message},
|
||||||
inbox::new_inbox_routing::Activity,
|
inbox::new_inbox_routing::Activity,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::UndoType;
|
use activitystreams::activity::kind::UndoType;
|
||||||
|
@ -12,7 +12,7 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoDeletePrivateMessage {
|
pub struct UndoDeletePrivateMessage {
|
||||||
actor: Url,
|
actor: Url,
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities_new::private_message::send_websocket_message,
|
activities::private_message::send_websocket_message,
|
||||||
inbox::new_inbox_routing::Activity,
|
inbox::new_inbox_routing::Activity,
|
||||||
};
|
};
|
||||||
use activitystreams::{activity::kind::UpdateType, base::BaseExt};
|
use activitystreams::{activity::kind::UpdateType, base::BaseExt};
|
||||||
|
@ -10,7 +10,7 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UpdatePrivateMessage {
|
pub struct UpdatePrivateMessage {
|
||||||
actor: Url,
|
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::{
|
use crate::inbox::new_inbox_routing::{Activity, SharedInboxActivities};
|
||||||
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 actix_web::{web, HttpRequest, HttpResponse};
|
use actix_web::{web, HttpRequest, HttpResponse};
|
||||||
use anyhow::{anyhow, Context};
|
use lemmy_utils::LemmyError;
|
||||||
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_websocket::LemmyContext;
|
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.
|
/// Handler for all incoming receive to community inboxes.
|
||||||
pub async fn community_inbox(
|
pub async fn community_inbox(
|
||||||
request: HttpRequest,
|
_request: HttpRequest,
|
||||||
input: web::Json<CommunityAcceptedActivities>,
|
_input: web::Json<Activity<Activity<SharedInboxActivities>>>,
|
||||||
path: web::Path<String>,
|
_path: web::Path<String>,
|
||||||
context: web::Data<LemmyContext>,
|
_context: web::Data<LemmyContext>,
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
let activity = input.into_inner();
|
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 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())
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,48 +1,29 @@
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::ActorAndObjectRefExt,
|
activity::ActorAndObjectRefExt,
|
||||||
base::{AsBase, BaseExt, Extends},
|
base::{AsBase, Extends},
|
||||||
object::AsObject,
|
object::AsObject,
|
||||||
public,
|
|
||||||
};
|
};
|
||||||
use actix_web::HttpRequest;
|
use actix_web::HttpRequest;
|
||||||
use anyhow::{anyhow, Context};
|
use anyhow::Context;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub::{
|
use lemmy_apub::{
|
||||||
check_is_apub_id_valid,
|
check_is_apub_id_valid,
|
||||||
extensions::signatures::verify_signature,
|
extensions::signatures::verify_signature,
|
||||||
fetcher::get_or_fetch_and_upsert_actor,
|
fetcher::get_or_fetch_and_upsert_actor,
|
||||||
get_activity_to_and_cc,
|
|
||||||
ActorType,
|
ActorType,
|
||||||
};
|
};
|
||||||
use lemmy_db_queries::{
|
use lemmy_db_queries::{source::activity::Activity_, DbPool};
|
||||||
source::{activity::Activity_, community::Community_},
|
use lemmy_db_schema::source::activity::Activity;
|
||||||
ApubObject,
|
use lemmy_utils::{location_info, LemmyError};
|
||||||
DbPool,
|
|
||||||
};
|
|
||||||
use lemmy_db_schema::source::{activity::Activity, community::Community, person::Person};
|
|
||||||
use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
|
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::fmt::Debug;
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
pub mod community_inbox;
|
pub mod community_inbox;
|
||||||
pub mod new_inbox_routing;
|
pub mod new_inbox_routing;
|
||||||
pub mod person_inbox;
|
pub mod person_inbox;
|
||||||
pub(crate) mod receive_for_community;
|
|
||||||
pub mod shared_inbox;
|
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(
|
pub(crate) async fn is_activity_already_known(
|
||||||
pool: &DbPool,
|
pool: &DbPool,
|
||||||
activity_id: &Url,
|
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>(
|
pub(crate) async fn inbox_verify_http_signature<T, Kind>(
|
||||||
activity: &T,
|
activity: &T,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
|
@ -91,64 +60,3 @@ where
|
||||||
verify_signature(&request, actor.as_ref())?;
|
verify_signature(&request, actor.as_ref())?;
|
||||||
Ok(actor)
|
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::{
|
comment::{
|
||||||
create::CreateComment,
|
create::CreateComment,
|
||||||
delete::DeleteComment,
|
delete::DeleteComment,
|
||||||
|
@ -12,8 +12,11 @@ use crate::activities_new::{
|
||||||
update::UpdateComment,
|
update::UpdateComment,
|
||||||
},
|
},
|
||||||
community::{
|
community::{
|
||||||
|
announce::AnnounceActivity,
|
||||||
|
block_user::BlockUserFromCommunity,
|
||||||
delete::DeleteCommunity,
|
delete::DeleteCommunity,
|
||||||
remove::RemoveCommunity,
|
remove::RemoveCommunity,
|
||||||
|
undo_block_user::UndoBlockUserFromCommunity,
|
||||||
undo_delete::UndoDeleteCommunity,
|
undo_delete::UndoDeleteCommunity,
|
||||||
undo_remove::UndoRemoveCommunity,
|
undo_remove::UndoRemoveCommunity,
|
||||||
update::UpdateCommunity,
|
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 {
|
pub enum SharedInboxActivities {
|
||||||
FollowCommunity(FollowCommunity),
|
FollowCommunity(FollowCommunity),
|
||||||
AcceptFollowCommunity(AcceptFollowCommunity),
|
AcceptFollowCommunity(AcceptFollowCommunity),
|
||||||
|
@ -97,11 +100,14 @@ pub enum SharedInboxActivities {
|
||||||
UndoRemovePost(UndoRemovePost),
|
UndoRemovePost(UndoRemovePost),
|
||||||
UndoLikePost(UndoLikePost),
|
UndoLikePost(UndoLikePost),
|
||||||
UndoDislikePost(UndoDislikePost),
|
UndoDislikePost(UndoDislikePost),
|
||||||
|
AnnounceActivity(AnnounceActivity),
|
||||||
UpdateCommunity(UpdateCommunity),
|
UpdateCommunity(UpdateCommunity),
|
||||||
DeleteCommunity(DeleteCommunity),
|
DeleteCommunity(DeleteCommunity),
|
||||||
RemoveCommunity(RemoveCommunity),
|
RemoveCommunity(RemoveCommunity),
|
||||||
UndoDeleteCommunity(UndoDeleteCommunity),
|
UndoDeleteCommunity(UndoDeleteCommunity),
|
||||||
UndoRemoveCommunity(UndoRemoveCommunity),
|
UndoRemoveCommunity(UndoRemoveCommunity),
|
||||||
|
BlockUserFromCommunity(BlockUserFromCommunity),
|
||||||
|
UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: can probably get rid of these?
|
// todo: can probably get rid of these?
|
||||||
|
|
|
@ -1,223 +1,13 @@
|
||||||
use crate::{
|
use crate::inbox::new_inbox_routing::{Activity, SharedInboxActivities};
|
||||||
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 actix_web::{web, HttpRequest, HttpResponse};
|
use actix_web::{web, HttpRequest, HttpResponse};
|
||||||
use anyhow::{anyhow, Context};
|
use lemmy_utils::LemmyError;
|
||||||
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_websocket::LemmyContext;
|
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(
|
pub async fn person_inbox(
|
||||||
_request: HttpRequest,
|
_request: HttpRequest,
|
||||||
input: web::Json<Activity<SharedInboxActivities>>,
|
_input: web::Json<Activity<Activity<SharedInboxActivities>>>,
|
||||||
_path: web::Path<String>,
|
_path: web::Path<String>,
|
||||||
context: web::Data<LemmyContext>,
|
_context: web::Data<LemmyContext>,
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
) -> 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!()
|
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::{
|
use crate::inbox::new_inbox_routing::{Activity, SharedInboxActivities};
|
||||||
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 actix_web::{web, HttpRequest, HttpResponse};
|
use actix_web::{web, HttpRequest, HttpResponse};
|
||||||
use anyhow::Context;
|
use lemmy_apub_lib::{ReceiveActivity, VerifyActivity};
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_utils::LemmyError;
|
||||||
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_websocket::LemmyContext;
|
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(
|
pub async fn shared_inbox(
|
||||||
request: HttpRequest,
|
_request: HttpRequest,
|
||||||
input: web::Json<AcceptedActivities>,
|
input: web::Json<Activity<SharedInboxActivities>>,
|
||||||
context: web::Data<LemmyContext>,
|
context: web::Data<LemmyContext>,
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
let activity = input.into_inner();
|
let activity = input.into_inner();
|
||||||
// First of all check the http signature
|
activity.inner.verify(&context).await?;
|
||||||
let request_counter = &mut 0;
|
let request_counter = &mut 0;
|
||||||
let actor = inbox_verify_http_signature(&activity, &context, request, request_counter).await?;
|
activity.inner.receive(&context, request_counter).await?;
|
||||||
|
todo!()
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
mod activities;
|
pub mod activities;
|
||||||
pub mod activities_new;
|
|
||||||
mod http;
|
mod http;
|
||||||
mod inbox;
|
mod inbox;
|
||||||
pub mod routes;
|
pub mod routes;
|
||||||
|
|
Loading…
Reference in a new issue