mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-05-11 14:32:39 +00:00
375d9a2a3c
* persistent activity queue * fixes * fixes * make federation workers function callable from outside * log federation instances * dead instance detection not needed here * taplo fmt * split federate bin/lib * minor fix * better logging * log * create struct to hold cancellable task for readability * use boxfuture for readability * reset submodule * fix * fix lint * swap * remove json column, use separate array columns instead * some review comments * make worker a struct for readability * minor readability * add local filter to community follower view * remove separate lemmy_federate entry point * fix remaining duration * address review comments mostly * fix lint * upgrade actitypub-fed to simpler interface * fix sql format * increase delays a bit * fixes after merge * remove selectable * fix instance selectable * add comment * start federation based on latest id at the time * rename federate process args * dead instances in one query * filter follow+report activities by local * remove synchronous federation remove activity sender queue * lint * fix federation tests by waiting for results to change * fix fed test * fix comment report * wait some more * Apply suggestions from code review Co-authored-by: SorteKanin <sortekanin@gmail.com> * fix most remaining tests * wait until private messages * fix community tests * fix community tests * move arg parse * use instance_id instead of domain in federation_queue_state table --------- Co-authored-by: Dessalines <dessalines@users.noreply.github.com> Co-authored-by: SorteKanin <sortekanin@gmail.com>
309 lines
9 KiB
Rust
309 lines
9 KiB
Rust
use crate::{
|
|
activities::{
|
|
community::send_activity_in_community,
|
|
send_lemmy_activity,
|
|
verify_is_public,
|
|
verify_mod_action,
|
|
verify_person,
|
|
verify_person_in_community,
|
|
},
|
|
activity_lists::AnnouncableActivities,
|
|
objects::{
|
|
comment::ApubComment,
|
|
community::ApubCommunity,
|
|
person::ApubPerson,
|
|
post::ApubPost,
|
|
private_message::ApubPrivateMessage,
|
|
},
|
|
protocol::{
|
|
activities::deletion::{delete::Delete, undo_delete::UndoDelete},
|
|
InCommunity,
|
|
},
|
|
};
|
|
use activitypub_federation::{
|
|
config::Data,
|
|
fetch::object_id::ObjectId,
|
|
kinds::public,
|
|
protocol::verification::verify_domains_match,
|
|
traits::{Actor, Object},
|
|
};
|
|
use lemmy_api_common::context::LemmyContext;
|
|
use lemmy_db_schema::{
|
|
newtypes::CommunityId,
|
|
source::{
|
|
activity::ActivitySendTargets,
|
|
comment::{Comment, CommentUpdateForm},
|
|
community::{Community, CommunityUpdateForm},
|
|
person::Person,
|
|
post::{Post, PostUpdateForm},
|
|
private_message::{PrivateMessage, PrivateMessageUpdateForm},
|
|
},
|
|
traits::Crud,
|
|
};
|
|
use lemmy_utils::error::LemmyError;
|
|
use std::ops::Deref;
|
|
use url::Url;
|
|
|
|
pub mod delete;
|
|
pub mod delete_user;
|
|
pub mod undo_delete;
|
|
|
|
/// Parameter `reason` being set indicates that this is a removal by a mod. If its unset, this
|
|
/// action was done by a normal user.
|
|
#[tracing::instrument(skip_all)]
|
|
pub(crate) async fn send_apub_delete_in_community(
|
|
actor: Person,
|
|
community: Community,
|
|
object: DeletableObjects,
|
|
reason: Option<String>,
|
|
deleted: bool,
|
|
context: &Data<LemmyContext>,
|
|
) -> Result<(), LemmyError> {
|
|
let actor = ApubPerson::from(actor);
|
|
let is_mod_action = reason.is_some();
|
|
let activity = if deleted {
|
|
let delete = Delete::new(&actor, object, public(), Some(&community), reason, context)?;
|
|
AnnouncableActivities::Delete(delete)
|
|
} else {
|
|
let undo = UndoDelete::new(&actor, object, public(), Some(&community), reason, context)?;
|
|
AnnouncableActivities::UndoDelete(undo)
|
|
};
|
|
send_activity_in_community(
|
|
activity,
|
|
&actor,
|
|
&community.into(),
|
|
ActivitySendTargets::empty(),
|
|
is_mod_action,
|
|
context,
|
|
)
|
|
.await
|
|
}
|
|
|
|
/// Parameter `reason` being set indicates that this is a removal by a mod. If its unset, this
|
|
/// action was done by a normal user.
|
|
#[tracing::instrument(skip_all)]
|
|
pub(crate) async fn send_apub_delete_in_community_new(
|
|
actor: Person,
|
|
community_id: CommunityId,
|
|
object: DeletableObjects,
|
|
reason: Option<String>,
|
|
deleted: bool,
|
|
context: Data<LemmyContext>,
|
|
) -> Result<(), LemmyError> {
|
|
let community = Community::read(&mut context.pool(), community_id).await?;
|
|
let actor = ApubPerson::from(actor);
|
|
let is_mod_action = reason.is_some();
|
|
let activity = if deleted {
|
|
let delete = Delete::new(&actor, object, public(), Some(&community), reason, &context)?;
|
|
AnnouncableActivities::Delete(delete)
|
|
} else {
|
|
let undo = UndoDelete::new(&actor, object, public(), Some(&community), reason, &context)?;
|
|
AnnouncableActivities::UndoDelete(undo)
|
|
};
|
|
send_activity_in_community(
|
|
activity,
|
|
&actor,
|
|
&community.into(),
|
|
ActivitySendTargets::empty(),
|
|
is_mod_action,
|
|
&context,
|
|
)
|
|
.await
|
|
}
|
|
|
|
#[tracing::instrument(skip_all)]
|
|
pub(crate) async fn send_apub_delete_private_message(
|
|
actor: &ApubPerson,
|
|
pm: PrivateMessage,
|
|
deleted: bool,
|
|
context: Data<LemmyContext>,
|
|
) -> Result<(), LemmyError> {
|
|
let recipient_id = pm.recipient_id;
|
|
let recipient: ApubPerson = Person::read(&mut context.pool(), recipient_id)
|
|
.await?
|
|
.into();
|
|
|
|
let deletable = DeletableObjects::PrivateMessage(pm.into());
|
|
let inbox = ActivitySendTargets::to_inbox(recipient.shared_inbox_or_inbox());
|
|
if deleted {
|
|
let delete: Delete = Delete::new(actor, deletable, recipient.id(), None, None, &context)?;
|
|
send_lemmy_activity(&context, delete, actor, inbox, true).await?;
|
|
} else {
|
|
let undo = UndoDelete::new(actor, deletable, recipient.id(), None, None, &context)?;
|
|
send_lemmy_activity(&context, undo, actor, inbox, true).await?;
|
|
};
|
|
Ok(())
|
|
}
|
|
|
|
pub enum DeletableObjects {
|
|
Community(ApubCommunity),
|
|
Comment(ApubComment),
|
|
Post(ApubPost),
|
|
PrivateMessage(ApubPrivateMessage),
|
|
}
|
|
|
|
impl DeletableObjects {
|
|
#[tracing::instrument(skip_all)]
|
|
pub(crate) async fn read_from_db(
|
|
ap_id: &Url,
|
|
context: &Data<LemmyContext>,
|
|
) -> Result<DeletableObjects, LemmyError> {
|
|
if let Some(c) = ApubCommunity::read_from_id(ap_id.clone(), context).await? {
|
|
return Ok(DeletableObjects::Community(c));
|
|
}
|
|
if let Some(p) = ApubPost::read_from_id(ap_id.clone(), context).await? {
|
|
return Ok(DeletableObjects::Post(p));
|
|
}
|
|
if let Some(c) = ApubComment::read_from_id(ap_id.clone(), context).await? {
|
|
return Ok(DeletableObjects::Comment(c));
|
|
}
|
|
if let Some(p) = ApubPrivateMessage::read_from_id(ap_id.clone(), context).await? {
|
|
return Ok(DeletableObjects::PrivateMessage(p));
|
|
}
|
|
Err(diesel::NotFound.into())
|
|
}
|
|
|
|
pub(crate) fn id(&self) -> Url {
|
|
match self {
|
|
DeletableObjects::Community(c) => c.id(),
|
|
DeletableObjects::Comment(c) => c.ap_id.clone().into(),
|
|
DeletableObjects::Post(p) => p.ap_id.clone().into(),
|
|
DeletableObjects::PrivateMessage(p) => p.ap_id.clone().into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tracing::instrument(skip_all)]
|
|
pub(in crate::activities) async fn verify_delete_activity(
|
|
activity: &Delete,
|
|
is_mod_action: bool,
|
|
context: &Data<LemmyContext>,
|
|
) -> Result<(), LemmyError> {
|
|
let object = DeletableObjects::read_from_db(activity.object.id(), context).await?;
|
|
match object {
|
|
DeletableObjects::Community(community) => {
|
|
verify_is_public(&activity.to, &[])?;
|
|
if community.local {
|
|
// can only do this check for local community, in remote case it would try to fetch the
|
|
// deleted community (which fails)
|
|
verify_person_in_community(&activity.actor, &community, context).await?;
|
|
}
|
|
// community deletion is always a mod (or admin) action
|
|
verify_mod_action(&activity.actor, activity.object.id(), community.id, context).await?;
|
|
}
|
|
DeletableObjects::Post(p) => {
|
|
verify_is_public(&activity.to, &[])?;
|
|
verify_delete_post_or_comment(
|
|
&activity.actor,
|
|
&p.ap_id.clone().into(),
|
|
&activity.community(context).await?,
|
|
is_mod_action,
|
|
context,
|
|
)
|
|
.await?;
|
|
}
|
|
DeletableObjects::Comment(c) => {
|
|
verify_is_public(&activity.to, &[])?;
|
|
verify_delete_post_or_comment(
|
|
&activity.actor,
|
|
&c.ap_id.clone().into(),
|
|
&activity.community(context).await?,
|
|
is_mod_action,
|
|
context,
|
|
)
|
|
.await?;
|
|
}
|
|
DeletableObjects::PrivateMessage(_) => {
|
|
verify_person(&activity.actor, context).await?;
|
|
verify_domains_match(activity.actor.inner(), activity.object.id())?;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[tracing::instrument(skip_all)]
|
|
async fn verify_delete_post_or_comment(
|
|
actor: &ObjectId<ApubPerson>,
|
|
object_id: &Url,
|
|
community: &ApubCommunity,
|
|
is_mod_action: bool,
|
|
context: &Data<LemmyContext>,
|
|
) -> Result<(), LemmyError> {
|
|
verify_person_in_community(actor, community, context).await?;
|
|
if is_mod_action {
|
|
verify_mod_action(actor, object_id, community.id, context).await?;
|
|
} else {
|
|
// domain of post ap_id and post.creator ap_id are identical, so we just check the former
|
|
verify_domains_match(actor.inner(), object_id)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Write deletion or restoring of an object to the database, and send websocket message.
|
|
#[tracing::instrument(skip_all)]
|
|
async fn receive_delete_action(
|
|
object: &Url,
|
|
actor: &ObjectId<ApubPerson>,
|
|
deleted: bool,
|
|
context: &Data<LemmyContext>,
|
|
) -> Result<(), LemmyError> {
|
|
match DeletableObjects::read_from_db(object, context).await? {
|
|
DeletableObjects::Community(community) => {
|
|
if community.local {
|
|
let mod_: Person = actor.dereference(context).await?.deref().clone();
|
|
let object = DeletableObjects::Community(community.clone());
|
|
let c: Community = community.deref().clone();
|
|
send_apub_delete_in_community(mod_, c, object, None, true, context).await?;
|
|
}
|
|
|
|
Community::update(
|
|
&mut context.pool(),
|
|
community.id,
|
|
&CommunityUpdateForm {
|
|
deleted: Some(deleted),
|
|
..Default::default()
|
|
},
|
|
)
|
|
.await?;
|
|
}
|
|
DeletableObjects::Post(post) => {
|
|
if deleted != post.deleted {
|
|
Post::update(
|
|
&mut context.pool(),
|
|
post.id,
|
|
&PostUpdateForm {
|
|
deleted: Some(deleted),
|
|
..Default::default()
|
|
},
|
|
)
|
|
.await?;
|
|
}
|
|
}
|
|
DeletableObjects::Comment(comment) => {
|
|
if deleted != comment.deleted {
|
|
Comment::update(
|
|
&mut context.pool(),
|
|
comment.id,
|
|
&CommentUpdateForm {
|
|
deleted: Some(deleted),
|
|
..Default::default()
|
|
},
|
|
)
|
|
.await?;
|
|
}
|
|
}
|
|
DeletableObjects::PrivateMessage(pm) => {
|
|
PrivateMessage::update(
|
|
&mut context.pool(),
|
|
pm.id,
|
|
&PrivateMessageUpdateForm {
|
|
deleted: Some(deleted),
|
|
..Default::default()
|
|
},
|
|
)
|
|
.await?;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|