Merge different delete activities for better compatibility (fixes #2066) (#2073)

This commit is contained in:
Nutomic 2022-02-14 15:14:24 +00:00 committed by GitHub
parent dd865c5af5
commit 788924d7ff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 452 additions and 568 deletions

View file

@ -7,7 +7,7 @@ use lemmy_api_common::{
get_local_user_view_from_jwt, get_local_user_view_from_jwt,
is_mod_or_admin, is_mod_or_admin,
}; };
use lemmy_apub::activities::deletion::{send_apub_delete, send_apub_remove, DeletableObjects}; use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
comment::Comment, comment::Comment,
@ -83,21 +83,7 @@ impl PerformCrud for DeleteComment {
) )
.await?; .await?;
// Send the apub message let res = send_comment_ws_message(
let community = blocking(context.pool(), move |conn| {
Community::read(conn, orig_comment.post.community_id)
})
.await??;
send_apub_delete(
&local_user_view.person.clone().into(),
&community.clone().into(),
DeletableObjects::Comment(Box::new(updated_comment.into())),
deleted,
context,
)
.await?;
send_comment_ws_message(
data.comment_id, data.comment_id,
UserOperationCrud::DeleteComment, UserOperationCrud::DeleteComment,
websocket_id, websocket_id,
@ -106,7 +92,25 @@ impl PerformCrud for DeleteComment {
recipient_ids, recipient_ids,
context, context,
) )
.await .await?;
// Send the apub message
let community = blocking(context.pool(), move |conn| {
Community::read(conn, orig_comment.post.community_id)
})
.await??;
let deletable = DeletableObjects::Comment(Box::new(updated_comment.clone().into()));
send_apub_delete_in_community(
local_user_view.person,
community,
deletable,
None,
deleted,
context,
)
.await?;
Ok(res)
} }
} }
@ -178,22 +182,7 @@ impl PerformCrud for RemoveComment {
) )
.await?; .await?;
// Send the apub message let res = send_comment_ws_message(
let community = blocking(context.pool(), move |conn| {
Community::read(conn, orig_comment.post.community_id)
})
.await??;
send_apub_remove(
&local_user_view.person.clone().into(),
&community.into(),
DeletableObjects::Comment(Box::new(updated_comment.into())),
data.reason.clone().unwrap_or_else(|| "".to_string()),
removed,
context,
)
.await?;
send_comment_ws_message(
data.comment_id, data.comment_id,
UserOperationCrud::RemoveComment, UserOperationCrud::RemoveComment,
websocket_id, websocket_id,
@ -202,6 +191,24 @@ impl PerformCrud for RemoveComment {
recipient_ids, recipient_ids,
context, context,
) )
.await .await?;
// Send the apub message
let community = blocking(context.pool(), move |conn| {
Community::read(conn, orig_comment.post.community_id)
})
.await??;
let deletable = DeletableObjects::Comment(Box::new(updated_comment.clone().into()));
send_apub_delete_in_community(
local_user_view.person,
community,
deletable,
data.reason.clone().or_else(|| Some("".to_string())),
removed,
context,
)
.await?;
Ok(res)
} }
} }

View file

@ -1,7 +1,7 @@
use crate::PerformCrud; use crate::PerformCrud;
use actix_web::web::Data; use actix_web::web::Data;
use lemmy_api_common::{blocking, community::*, get_local_user_view_from_jwt, is_admin}; use lemmy_api_common::{blocking, community::*, get_local_user_view_from_jwt, is_admin};
use lemmy_apub::activities::deletion::{send_apub_delete, send_apub_remove, DeletableObjects}; use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
community::Community, community::Community,
@ -49,24 +49,28 @@ impl PerformCrud for DeleteCommunity {
.map_err(LemmyError::from) .map_err(LemmyError::from)
.map_err(|e| e.with_message("couldnt_update_community"))?; .map_err(|e| e.with_message("couldnt_update_community"))?;
// Send apub messages let res = send_community_ws_message(
send_apub_delete(
&local_user_view.person.clone().into(),
&updated_community.clone().into(),
DeletableObjects::Community(Box::new(updated_community.into())),
deleted,
context,
)
.await?;
send_community_ws_message(
data.community_id, data.community_id,
UserOperationCrud::DeleteCommunity, UserOperationCrud::DeleteCommunity,
websocket_id, websocket_id,
Some(local_user_view.person.id), Some(local_user_view.person.id),
context, context,
) )
.await .await?;
// Send apub messages
let deletable = DeletableObjects::Community(Box::new(updated_community.clone().into()));
send_apub_delete_in_community(
local_user_view.person,
updated_community,
deletable,
None,
deleted,
context,
)
.await?;
Ok(res)
} }
} }
@ -111,24 +115,26 @@ impl PerformCrud for RemoveCommunity {
}) })
.await??; .await??;
// Apub messages let res = send_community_ws_message(
send_apub_remove(
&local_user_view.person.clone().into(),
&updated_community.clone().into(),
DeletableObjects::Community(Box::new(updated_community.into())),
data.reason.clone().unwrap_or_else(|| "".to_string()),
removed,
context,
)
.await?;
send_community_ws_message(
data.community_id, data.community_id,
UserOperationCrud::RemoveCommunity, UserOperationCrud::RemoveCommunity,
websocket_id, websocket_id,
Some(local_user_view.person.id), Some(local_user_view.person.id),
context, context,
) )
.await .await?;
// Apub messages
let deletable = DeletableObjects::Community(Box::new(updated_community.clone().into()));
send_apub_delete_in_community(
local_user_view.person,
updated_community,
deletable,
data.reason.clone().or_else(|| Some("".to_string())),
removed,
context,
)
.await?;
Ok(res)
} }
} }

View file

@ -8,7 +8,7 @@ use lemmy_api_common::{
is_mod_or_admin, is_mod_or_admin,
post::*, post::*,
}; };
use lemmy_apub::activities::deletion::{send_apub_delete, send_apub_remove, DeletableObjects}; use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
community::Community, community::Community,
@ -63,28 +63,31 @@ impl PerformCrud for DeletePost {
}) })
.await??; .await??;
// apub updates let res = send_post_ws_message(
let community = blocking(context.pool(), move |conn| {
Community::read(conn, orig_post.community_id)
})
.await??;
send_apub_delete(
&local_user_view.person.clone().into(),
&community.into(),
DeletableObjects::Post(Box::new(updated_post.into())),
deleted,
context,
)
.await?;
send_post_ws_message(
data.post_id, data.post_id,
UserOperationCrud::DeletePost, UserOperationCrud::DeletePost,
websocket_id, websocket_id,
Some(local_user_view.person.id), Some(local_user_view.person.id),
context, context,
) )
.await .await?;
// apub updates
let community = blocking(context.pool(), move |conn| {
Community::read(conn, orig_post.community_id)
})
.await??;
let deletable = DeletableObjects::Post(Box::new(updated_post.into()));
send_apub_delete_in_community(
local_user_view.person,
community,
deletable,
None,
deleted,
context,
)
.await?;
Ok(res)
} }
} }
@ -140,28 +143,30 @@ impl PerformCrud for RemovePost {
}) })
.await??; .await??;
// apub updates let res = send_post_ws_message(
let community = blocking(context.pool(), move |conn| {
Community::read(conn, orig_post.community_id)
})
.await??;
send_apub_remove(
&local_user_view.person.clone().into(),
&community.into(),
DeletableObjects::Post(Box::new(updated_post.into())),
data.reason.clone().unwrap_or_else(|| "".to_string()),
removed,
context,
)
.await?;
send_post_ws_message(
data.post_id, data.post_id,
UserOperationCrud::RemovePost, UserOperationCrud::RemovePost,
websocket_id, websocket_id,
Some(local_user_view.person.id), Some(local_user_view.person.id),
context, context,
) )
.await .await?;
// apub updates
let community = blocking(context.pool(), move |conn| {
Community::read(conn, orig_post.community_id)
})
.await??;
let deletable = DeletableObjects::Post(Box::new(updated_post.into()));
send_apub_delete_in_community(
local_user_view.person,
community,
deletable,
data.reason.clone().or_else(|| Some("".to_string())),
removed,
context,
)
.await?;
Ok(res)
} }
} }

View file

@ -10,7 +10,7 @@ use lemmy_api_common::{
use lemmy_apub::{ use lemmy_apub::{
generate_local_apub_endpoint, generate_local_apub_endpoint,
protocol::activities::{ protocol::activities::{
private_message::create_or_update::CreateOrUpdatePrivateMessage, create_or_update::private_message::CreateOrUpdatePrivateMessage,
CreateOrUpdateType, CreateOrUpdateType,
}, },
EndpointType, EndpointType,

View file

@ -5,14 +5,8 @@ use lemmy_api_common::{
get_local_user_view_from_jwt, get_local_user_view_from_jwt,
person::{DeletePrivateMessage, PrivateMessageResponse}, person::{DeletePrivateMessage, PrivateMessageResponse},
}; };
use lemmy_apub::protocol::activities::private_message::{ use lemmy_apub::activities::deletion::send_apub_delete_private_message;
delete::DeletePrivateMessage as DeletePrivateMessageApub, use lemmy_db_schema::{source::private_message::PrivateMessage, traits::Crud};
undo_delete::UndoDeletePrivateMessage,
};
use lemmy_db_schema::{
source::private_message::PrivateMessage,
traits::{Crud, DeleteableOrRemoveable},
};
use lemmy_utils::{ConnectionId, LemmyError}; use lemmy_utils::{ConnectionId, LemmyError};
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
@ -51,23 +45,13 @@ impl PerformCrud for DeletePrivateMessage {
.map_err(|e| e.with_message("couldnt_update_private_message"))?; .map_err(|e| e.with_message("couldnt_update_private_message"))?;
// Send the apub update // Send the apub update
if data.deleted { send_apub_delete_private_message(
DeletePrivateMessageApub::send( &local_user_view.person.into(),
&local_user_view.person.into(), updated_private_message,
&updated_private_message data.deleted,
.blank_out_deleted_or_removed_info() context,
.into(), )
context, .await?;
)
.await?;
} else {
UndoDeletePrivateMessage::send(
&local_user_view.person.into(),
&updated_private_message.into(),
context,
)
.await?;
}
let op = UserOperationCrud::DeletePrivateMessage; let op = UserOperationCrud::DeletePrivateMessage;
send_pm_ws_message(data.private_message_id, op, websocket_id, context).await send_pm_ws_message(data.private_message_id, op, websocket_id, context).await

View file

@ -6,7 +6,7 @@ use lemmy_api_common::{
person::{EditPrivateMessage, PrivateMessageResponse}, person::{EditPrivateMessage, PrivateMessageResponse},
}; };
use lemmy_apub::protocol::activities::{ use lemmy_apub::protocol::activities::{
private_message::create_or_update::CreateOrUpdatePrivateMessage, create_or_update::private_message::CreateOrUpdatePrivateMessage,
CreateOrUpdateType, CreateOrUpdateType,
}; };
use lemmy_db_schema::{source::private_message::PrivateMessage, traits::Crud}; use lemmy_db_schema::{source::private_message::PrivateMessage, traits::Crud};

View file

@ -3,10 +3,7 @@
"to": [ "to": [
"https://www.w3.org/ns/activitystreams#Public" "https://www.w3.org/ns/activitystreams#Public"
], ],
"object": { "object": "http://ds9.lemmy.ml/post/1",
"id": "http://ds9.lemmy.ml/post/1",
"type": "Tombstone"
},
"cc": [ "cc": [
"http://enterprise.lemmy.ml/c/main" "http://enterprise.lemmy.ml/c/main"
], ],

View file

@ -3,10 +3,7 @@
"to": [ "to": [
"https://www.w3.org/ns/activitystreams#Public" "https://www.w3.org/ns/activitystreams#Public"
], ],
"object": { "object": "http://ds9.lemmy.ml/comment/1",
"id": "http://ds9.lemmy.ml/comment/1",
"type": "Tombstone"
},
"cc": [ "cc": [
"http://enterprise.lemmy.ml/c/main" "http://enterprise.lemmy.ml/c/main"
], ],

View file

@ -8,10 +8,7 @@
"to": [ "to": [
"https://www.w3.org/ns/activitystreams#Public" "https://www.w3.org/ns/activitystreams#Public"
], ],
"object": { "object": "http://ds9.lemmy.ml/post/1",
"id": "http://ds9.lemmy.ml/post/1",
"type": "Tombstone"
},
"cc": [ "cc": [
"http://enterprise.lemmy.ml/c/main" "http://enterprise.lemmy.ml/c/main"
], ],

View file

@ -8,10 +8,7 @@
"to": [ "to": [
"https://www.w3.org/ns/activitystreams#Public" "https://www.w3.org/ns/activitystreams#Public"
], ],
"object": { "object": "http://ds9.lemmy.ml/comment/1",
"id": "http://ds9.lemmy.ml/comment/1",
"type": "Tombstone"
},
"cc": [ "cc": [
"http://enterprise.lemmy.ml/c/main" "http://enterprise.lemmy.ml/c/main"
], ],

View file

@ -0,0 +1,26 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"ostatus": "http://ostatus.org#",
"atomUri": "ostatus:atomUri"
}
],
"id": "https://mastodon.madrid/users/felix/statuses/107773559874184870#delete",
"type": "Delete",
"actor": "https://mastodon.madrid/users/felix",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"object": {
"id": "https://mastodon.madrid/users/felix/statuses/107773559874184870",
"type": "Tombstone",
"atomUri": "https://mastodon.madrid/users/felix/statuses/107773559874184870"
},
"signature": {
"type": "RsaSignature2017",
"creator": "https://mastodon.madrid/users/felix#main-key",
"created": "2022-02-10T11:54:18Z",
"signatureValue": "NjGnbkvouSP/cSusR7+sz39iEYxWXCu6nFmBXU3t8ETPkmbpMF5ASeJixXvpTOqbOfkMoWfXncw+jDsbqZ3ELaHGG1gZ5wHWym7mk7YCjQokpF3oPhTWmlEJCVKgewXMrfI4Ok8GGsUMGzuki9EyBDGc/UNBMEAhcxV5Huu7QSQDowcbIwxS3ImxFmtKFceh6mv/kMiXUerCgkYSm6rYZeXZGMTUpvcn9gP6X6Ed6UsrLjCSb3Fj0Naz7LHtzZXRSZDZF/SX2Vw/xKJIgEGzSCv+LKZGvEEkK8PPfMJJhi8cBJebkqOnBGtE6gYK2z2cm/oGorZtXU2L05pXmLAlYQ=="
}
}

View file

@ -0,0 +1,35 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://greenish.red/schemas/litepub-0.1.jsonld",
{
"@language": "und"
}
],
"actor": "https://greenish.red/users/vpzom",
"attachment": [],
"attributedTo": "https://greenish.red/users/vpzom",
"cc": [],
"conversation": null,
"id": "https://greenish.red/activities/52f0b259-596e-429f-8a1b-c0b455f8932b",
"object": "https://greenish.red/objects/38e2b983-ebf5-4387-9bc2-3b80305469c9",
"tag": [
{
"href": "https://voyager.lemmy.ml/c/main",
"name": "@main@voyager.lemmy.ml",
"type": "Mention"
},
{
"href": "https://voyager.lemmy.ml/u/dess_voy_41u2",
"name": "@dess_voy_41u2@voyager.lemmy.ml",
"type": "Mention"
}
],
"to": [
"https://greenish.red/users/vpzom/followers",
"https://voyager.lemmy.ml/c/main",
"https://voyager.lemmy.ml/u/dess_voy_41u2",
"https://www.w3.org/ns/activitystreams#Public"
],
"type": "Delete"
}

View file

@ -1,8 +1,8 @@
use crate::{ use crate::{
activities::{ activities::{
check_community_deleted_or_removed, check_community_deleted_or_removed,
comment::get_notif_recipients,
community::{announce::GetCommunity, send_activity_in_community}, community::{announce::GetCommunity, send_activity_in_community},
create_or_update::get_comment_notif_recipients,
generate_activity_id, generate_activity_id,
verify_activity, verify_activity,
verify_is_public, verify_is_public,
@ -114,7 +114,7 @@ impl ActivityHandler for CreateOrUpdateComment {
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let comment = ApubComment::from_apub(self.object, context, request_counter).await?; let comment = ApubComment::from_apub(self.object, context, request_counter).await?;
let do_send_email = self.kind == CreateOrUpdateType::Create; let do_send_email = self.kind == CreateOrUpdateType::Create;
let recipients = get_notif_recipients( let recipients = get_comment_notif_recipients(
&self.actor, &self.actor,
&comment, &comment,
do_send_email, do_send_email,

View file

@ -9,10 +9,12 @@ use lemmy_db_schema::{
use lemmy_utils::{utils::scrape_text_for_mentions, LemmyError}; use lemmy_utils::{utils::scrape_text_for_mentions, LemmyError};
use lemmy_websocket::{send::send_local_notifs, LemmyContext}; use lemmy_websocket::{send::send_local_notifs, LemmyContext};
pub mod create_or_update; pub mod comment;
pub mod post;
pub mod private_message;
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
async fn get_notif_recipients( async fn get_comment_notif_recipients(
actor: &ObjectId<ApubPerson>, actor: &ObjectId<ApubPerson>,
comment: &Comment, comment: &Comment,
do_send_email: bool, do_send_email: bool,

View file

@ -2,7 +2,7 @@ use crate::{
activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person}, activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
objects::{person::ApubPerson, private_message::ApubPrivateMessage}, objects::{person::ApubPerson, private_message::ApubPrivateMessage},
protocol::activities::{ protocol::activities::{
private_message::create_or_update::CreateOrUpdatePrivateMessage, create_or_update::private_message::CreateOrUpdatePrivateMessage,
CreateOrUpdateType, CreateOrUpdateType,
}, },
}; };

View file

@ -1,22 +1,17 @@
use crate::{ use crate::{
activities::{ activities::{
community::{announce::GetCommunity, send_activity_in_community}, community::announce::GetCommunity,
deletion::{receive_delete_action, verify_delete_activity, DeletableObjects}, deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
generate_activity_id, generate_activity_id,
verify_activity, verify_activity,
verify_is_public,
}, },
activity_lists::AnnouncableActivities,
objects::{community::ApubCommunity, person::ApubPerson}, objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::deletion::delete::Delete, protocol::activities::deletion::delete::{Delete, IdOrNestedObject},
}; };
use activitystreams_kinds::{activity::DeleteType, public}; use activitystreams_kinds::activity::DeleteType;
use anyhow::anyhow;
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::{ use lemmy_apub_lib::{data::Data, object_id::ObjectId, traits::ActivityHandler};
data::Data,
object_id::ObjectId,
traits::{ActivityHandler, ActorType},
};
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
comment::Comment, comment::Comment,
@ -29,6 +24,7 @@ use lemmy_db_schema::{
ModRemovePost, ModRemovePost,
ModRemovePostForm, ModRemovePostForm,
}, },
person::Person,
post::Post, post::Post,
}, },
traits::Crud, traits::Crud,
@ -51,18 +47,8 @@ impl ActivityHandler for Delete {
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_is_public(&self.to, &[])?;
verify_activity(&self.id, self.actor.inner(), &context.settings())?; verify_activity(&self.id, self.actor.inner(), &context.settings())?;
let community = self.get_community(context, request_counter).await?; verify_delete_activity(self, self.summary.is_some(), context, request_counter).await?;
verify_delete_activity(
&self.object.id,
&self.actor,
&community,
self.summary.is_some(),
context,
request_counter,
)
.await?;
Ok(()) Ok(())
} }
@ -82,53 +68,50 @@ impl ActivityHandler for Delete {
}; };
receive_remove_action( receive_remove_action(
&self.actor, &self.actor,
&self.object.id, self.object.id(),
reason, reason,
context, context,
request_counter, request_counter,
) )
.await .await
} else { } else {
receive_delete_action(&self.object.id, &self.actor, true, context, request_counter).await receive_delete_action(
self.object.id(),
&self.actor,
true,
context,
request_counter,
)
.await
} }
} }
} }
impl Delete { impl Delete {
pub(in crate::activities::deletion) fn new( pub(in crate::activities::deletion) fn new(
actor: &ApubPerson, actor: &Person,
object: DeletableObjects, object: DeletableObjects,
to: Url,
community: Option<&Community>,
summary: Option<String>, summary: Option<String>,
context: &LemmyContext, context: &LemmyContext,
) -> Result<Delete, LemmyError> { ) -> Result<Delete, LemmyError> {
let id = generate_activity_id(
DeleteType::Delete,
&context.settings().get_protocol_and_hostname(),
)?;
let cc: Option<Url> = community.map(|c| c.actor_id.clone().into());
Ok(Delete { Ok(Delete {
actor: ObjectId::new(actor.actor_id()), actor: ObjectId::new(actor.actor_id.clone()),
to: vec![public()], to: vec![to],
object: object.to_tombstone()?, object: IdOrNestedObject::Id(object.id()),
cc: cc.into_iter().collect(),
kind: DeleteType::Delete, kind: DeleteType::Delete,
summary, summary,
id: generate_activity_id( id,
DeleteType::Delete,
&context.settings().get_protocol_and_hostname(),
)?,
unparsed: Default::default(), unparsed: Default::default(),
}) })
} }
#[tracing::instrument(skip_all)]
pub(in crate::activities::deletion) async fn send(
actor: &ApubPerson,
community: &ApubCommunity,
object: DeletableObjects,
summary: Option<String>,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let delete = Delete::new(actor, object, summary, context)?;
let delete_id = delete.id.clone();
let activity = AnnouncableActivities::Delete(delete);
send_activity_in_community(activity, &delete_id, actor, community, vec![], context).await
}
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
@ -204,6 +187,7 @@ pub(in crate::activities) async fn receive_remove_action(
send_comment_ws_message_simple(removed_comment.id, RemoveComment, context).await?; send_comment_ws_message_simple(removed_comment.id, RemoveComment, context).await?;
} }
DeletableObjects::PrivateMessage(_) => unimplemented!(),
} }
Ok(()) Ok(())
} }
@ -216,13 +200,16 @@ impl GetCommunity for Delete {
context: &LemmyContext, context: &LemmyContext,
_request_counter: &mut i32, _request_counter: &mut i32,
) -> Result<ApubCommunity, LemmyError> { ) -> Result<ApubCommunity, LemmyError> {
let community_id = match DeletableObjects::read_from_db(&self.object.id, context).await? { let community_id = match DeletableObjects::read_from_db(self.object.id(), context).await? {
DeletableObjects::Community(c) => c.id, DeletableObjects::Community(c) => c.id,
DeletableObjects::Comment(c) => { DeletableObjects::Comment(c) => {
let post = blocking(context.pool(), move |conn| Post::read(conn, c.post_id)).await??; let post = blocking(context.pool(), move |conn| Post::read(conn, c.post_id)).await??;
post.community_id post.community_id
} }
DeletableObjects::Post(p) => p.community_id, DeletableObjects::Post(p) => p.community_id,
DeletableObjects::PrivateMessage(_) => {
return Err(anyhow!("Private message is not part of community").into())
}
}; };
let community = blocking(context.pool(), move |conn| { let community = blocking(context.pool(), move |conn| {
Community::read(conn, community_id) Community::read(conn, community_id)

View file

@ -1,62 +1,117 @@
use crate::{ use crate::{
activities::{verify_mod_action, verify_person_in_community}, activities::{
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost}, community::{announce::GetCommunity, send_activity_in_community},
protocol::{ send_lemmy_activity,
activities::deletion::{delete::Delete, undo_delete::UndoDelete}, verify_is_public,
objects::tombstone::Tombstone, 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},
}; };
use activitystreams_kinds::public;
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::{object_id::ObjectId, traits::ApubObject, verify::verify_domains_match}; use lemmy_apub_lib::{
use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post}; object_id::ObjectId,
traits::{ActorType, ApubObject},
verify::verify_domains_match,
};
use lemmy_db_schema::{
source::{
comment::Comment,
community::Community,
person::Person,
post::Post,
private_message::PrivateMessage,
},
traits::Crud,
};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::{ use lemmy_websocket::{
send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message}, send::{
send_comment_ws_message_simple,
send_community_ws_message,
send_pm_ws_message,
send_post_ws_message,
},
LemmyContext, LemmyContext,
UserOperationCrud, UserOperationCrud,
}; };
use std::ops::Deref;
use url::Url; use url::Url;
pub mod delete; pub mod delete;
pub mod undo_delete; 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)] #[tracing::instrument(skip_all)]
pub async fn send_apub_delete( pub async fn send_apub_delete_in_community(
actor: &ApubPerson, actor: Person,
community: &ApubCommunity, community: Community,
object: DeletableObjects, object: DeletableObjects,
reason: Option<String>,
deleted: bool, deleted: bool,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
if deleted { let (id, activity) = if deleted {
Delete::send(actor, community, object, None, context).await let delete = Delete::new(&actor, object, public(), Some(&community), reason, context)?;
(delete.id.clone(), AnnouncableActivities::Delete(delete))
} else { } else {
UndoDelete::send(actor, community, object, None, context).await let undo = UndoDelete::new(&actor, object, public(), Some(&community), reason, context)?;
} (undo.id.clone(), AnnouncableActivities::UndoDelete(undo))
};
send_activity_in_community(
activity,
&id,
&ApubPerson::from(actor),
&community.into(),
vec![],
context,
)
.await
} }
// TODO: remove reason is actually optional in lemmy. we set an empty string in that case, but its
// ugly
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub async fn send_apub_remove( pub async fn send_apub_delete_private_message(
actor: &ApubPerson, actor: &ApubPerson,
community: &ApubCommunity, pm: PrivateMessage,
object: DeletableObjects, deleted: bool,
reason: String,
removed: bool,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
if removed { let recipient_id = pm.recipient_id;
Delete::send(actor, community, object, Some(reason), context).await let recipient: ApubPerson =
blocking(context.pool(), move |conn| Person::read(conn, recipient_id))
.await??
.into();
let deletable = DeletableObjects::PrivateMessage(Box::new(pm.into()));
let inbox = vec![recipient.shared_inbox_or_inbox_url()];
if deleted {
let delete = Delete::new(actor, deletable, recipient.actor_id(), None, None, context)?;
let id = delete.id.clone();
send_lemmy_activity(context, &delete, &id, actor, inbox, true).await?;
} else { } else {
UndoDelete::send(actor, community, object, Some(reason), context).await let undo = UndoDelete::new(actor, deletable, recipient.actor_id(), None, None, context)?;
} let id = undo.id.clone();
send_lemmy_activity(context, &undo, &id, actor, inbox, true).await?;
};
Ok(())
} }
pub enum DeletableObjects { pub enum DeletableObjects {
Community(Box<ApubCommunity>), Community(Box<ApubCommunity>),
Comment(Box<ApubComment>), Comment(Box<ApubComment>),
Post(Box<ApubPost>), Post(Box<ApubPost>),
PrivateMessage(Box<ApubPrivateMessage>),
} }
impl DeletableObjects { impl DeletableObjects {
@ -74,43 +129,47 @@ impl DeletableObjects {
if let Some(c) = ApubComment::read_from_apub_id(ap_id.clone(), context).await? { if let Some(c) = ApubComment::read_from_apub_id(ap_id.clone(), context).await? {
return Ok(DeletableObjects::Comment(Box::new(c))); return Ok(DeletableObjects::Comment(Box::new(c)));
} }
if let Some(p) = ApubPrivateMessage::read_from_apub_id(ap_id.clone(), context).await? {
return Ok(DeletableObjects::PrivateMessage(Box::new(p)));
}
Err(diesel::NotFound.into()) Err(diesel::NotFound.into())
} }
pub(crate) fn to_tombstone(&self) -> Result<Tombstone, LemmyError> { pub(crate) fn id(&self) -> Url {
match self { match self {
DeletableObjects::Community(c) => c.to_tombstone(), DeletableObjects::Community(c) => c.actor_id(),
DeletableObjects::Comment(c) => c.to_tombstone(), DeletableObjects::Comment(c) => c.ap_id.clone().into(),
DeletableObjects::Post(p) => p.to_tombstone(), DeletableObjects::Post(p) => p.ap_id.clone().into(),
DeletableObjects::PrivateMessage(p) => p.ap_id.clone().into(),
} }
} }
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub(in crate::activities) async fn verify_delete_activity( pub(in crate::activities) async fn verify_delete_activity(
object: &Url, activity: &Delete,
actor: &ObjectId<ApubPerson>,
community: &ApubCommunity,
is_mod_action: bool, is_mod_action: bool,
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let object = DeletableObjects::read_from_db(object, context).await?; let object = DeletableObjects::read_from_db(activity.object.id(), context).await?;
match object { match object {
DeletableObjects::Community(community) => { DeletableObjects::Community(community) => {
verify_is_public(&activity.to, &[])?;
if community.local { if community.local {
// can only do this check for local community, in remote case it would try to fetch the // can only do this check for local community, in remote case it would try to fetch the
// deleted community (which fails) // deleted community (which fails)
verify_person_in_community(actor, &community, context, request_counter).await?; verify_person_in_community(&activity.actor, &community, context, request_counter).await?;
} }
// community deletion is always a mod (or admin) action // community deletion is always a mod (or admin) action
verify_mod_action(actor, &community, context, request_counter).await?; verify_mod_action(&activity.actor, &community, context, request_counter).await?;
} }
DeletableObjects::Post(p) => { DeletableObjects::Post(p) => {
verify_delete_activity_post_or_comment( verify_is_public(&activity.to, &[])?;
actor, verify_delete_post_or_comment(
&activity.actor,
&p.ap_id.clone().into(), &p.ap_id.clone().into(),
community, &activity.get_community(context, request_counter).await?,
is_mod_action, is_mod_action,
context, context,
request_counter, request_counter,
@ -118,22 +177,27 @@ pub(in crate::activities) async fn verify_delete_activity(
.await?; .await?;
} }
DeletableObjects::Comment(c) => { DeletableObjects::Comment(c) => {
verify_delete_activity_post_or_comment( verify_is_public(&activity.to, &[])?;
actor, verify_delete_post_or_comment(
&activity.actor,
&c.ap_id.clone().into(), &c.ap_id.clone().into(),
community, &activity.get_community(context, request_counter).await?,
is_mod_action, is_mod_action,
context, context,
request_counter, request_counter,
) )
.await?; .await?;
} }
DeletableObjects::PrivateMessage(_) => {
verify_person(&activity.actor, context, request_counter).await?;
verify_domains_match(activity.actor.inner(), activity.object.id())?;
}
} }
Ok(()) Ok(())
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
async fn verify_delete_activity_post_or_comment( async fn verify_delete_post_or_comment(
actor: &ObjectId<ApubPerson>, actor: &ObjectId<ApubPerson>,
object_id: &Url, object_id: &Url,
community: &ApubCommunity, community: &ApubCommunity,
@ -152,8 +216,6 @@ async fn verify_delete_activity_post_or_comment(
} }
/// Write deletion or restoring of an object to the database, and send websocket message. /// Write deletion or restoring of an object to the database, and send websocket message.
/// TODO: we should do something similar for receive_remove_action(), but its much more complicated
/// because of the mod log
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
async fn receive_delete_action( async fn receive_delete_action(
object: &Url, object: &Url,
@ -165,11 +227,14 @@ async fn receive_delete_action(
match DeletableObjects::read_from_db(object, context).await? { match DeletableObjects::read_from_db(object, context).await? {
DeletableObjects::Community(community) => { DeletableObjects::Community(community) => {
if community.local { if community.local {
let mod_ = actor let mod_: Person = actor
.dereference(context, context.client(), request_counter) .dereference(context, context.client(), request_counter)
.await?; .await?
.deref()
.clone();
let object = DeletableObjects::Community(community.clone()); let object = DeletableObjects::Community(community.clone());
send_apub_delete(&mod_, &community.clone(), object, true, context).await?; let c: Community = community.deref().deref().clone();
send_apub_delete_in_community(mod_, c, object, None, true, context).await?;
} }
let community = blocking(context.pool(), move |conn| { let community = blocking(context.pool(), move |conn| {
@ -215,6 +280,20 @@ async fn receive_delete_action(
.await?; .await?;
} }
} }
DeletableObjects::PrivateMessage(pm) => {
let deleted_private_message = blocking(context.pool(), move |conn| {
PrivateMessage::update_deleted(conn, pm.id, deleted)
})
.await??;
send_pm_ws_message(
deleted_private_message.id,
UserOperationCrud::DeletePrivateMessage,
None,
context,
)
.await?;
}
} }
Ok(()) Ok(())
} }

View file

@ -1,23 +1,17 @@
use crate::{ use crate::{
activities::{ activities::{
community::{announce::GetCommunity, send_activity_in_community}, community::announce::GetCommunity,
deletion::{receive_delete_action, verify_delete_activity, DeletableObjects}, deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
generate_activity_id, generate_activity_id,
verify_activity, verify_activity,
verify_is_public,
}, },
activity_lists::AnnouncableActivities, objects::community::ApubCommunity,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete}, protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete},
}; };
use activitystreams_kinds::{activity::UndoType, public}; use activitystreams_kinds::activity::UndoType;
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::{ use lemmy_apub_lib::{data::Data, object_id::ObjectId, traits::ActivityHandler};
data::Data, use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post};
object_id::ObjectId,
traits::{ActivityHandler, ActorType},
};
use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::{ use lemmy_websocket::{
send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message}, send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message},
@ -36,14 +30,10 @@ impl ActivityHandler for UndoDelete {
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_is_public(&self.to, &self.cc)?;
verify_activity(&self.id, self.actor.inner(), &context.settings())?; verify_activity(&self.id, self.actor.inner(), &context.settings())?;
self.object.verify(context, request_counter).await?; self.object.verify(context, request_counter).await?;
let community = self.get_community(context, request_counter).await?;
verify_delete_activity( verify_delete_activity(
&self.object.object.id, &self.object,
&self.actor,
&community,
self.object.summary.is_some(), self.object.summary.is_some(),
context, context,
request_counter, request_counter,
@ -59,10 +49,10 @@ impl ActivityHandler for UndoDelete {
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
if self.object.summary.is_some() { if self.object.summary.is_some() {
UndoDelete::receive_undo_remove_action(&self.object.object.id, context).await UndoDelete::receive_undo_remove_action(self.object.object.id(), context).await
} else { } else {
receive_delete_action( receive_delete_action(
&self.object.object.id, self.object.object.id(),
&self.actor, &self.actor,
false, false,
context, context,
@ -75,31 +65,30 @@ impl ActivityHandler for UndoDelete {
impl UndoDelete { impl UndoDelete {
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub(in crate::activities::deletion) async fn send( pub(in crate::activities::deletion) fn new(
actor: &ApubPerson, actor: &Person,
community: &ApubCommunity,
object: DeletableObjects, object: DeletableObjects,
to: Url,
community: Option<&Community>,
summary: Option<String>, summary: Option<String>,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<UndoDelete, LemmyError> {
let object = Delete::new(actor, object, summary, context)?; let object = Delete::new(actor, object, to.clone(), community, summary, context)?;
let id = generate_activity_id( let id = generate_activity_id(
UndoType::Undo, UndoType::Undo,
&context.settings().get_protocol_and_hostname(), &context.settings().get_protocol_and_hostname(),
)?; )?;
let undo = UndoDelete { let cc: Option<Url> = community.map(|c| c.actor_id.clone().into());
actor: ObjectId::new(actor.actor_id()), Ok(UndoDelete {
to: vec![public()], actor: ObjectId::new(actor.actor_id.clone()),
to: vec![to],
object, object,
cc: vec![community.actor_id()], cc: cc.into_iter().collect(),
kind: UndoType::Undo, kind: UndoType::Undo,
id: id.clone(), id,
unparsed: Default::default(), unparsed: Default::default(),
}; })
let activity = AnnouncableActivities::UndoDelete(undo);
send_activity_in_community(activity, &id, actor, community, vec![], context).await
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
@ -135,6 +124,7 @@ impl UndoDelete {
.await??; .await??;
send_comment_ws_message_simple(removed_comment.id, EditComment, context).await?; send_comment_ws_message_simple(removed_comment.id, EditComment, context).await?;
} }
DeletableObjects::PrivateMessage(_) => unimplemented!(),
} }
Ok(()) Ok(())
} }

View file

@ -26,12 +26,10 @@ use url::{ParseError, Url};
use uuid::Uuid; use uuid::Uuid;
pub mod block; pub mod block;
pub mod comment;
pub mod community; pub mod community;
pub mod create_or_update;
pub mod deletion; pub mod deletion;
pub mod following; pub mod following;
pub mod post;
pub mod private_message;
pub mod voting; pub mod voting;
/// Checks that the specified Url actually identifies a Person (by fetching it), and that the person /// Checks that the specified Url actually identifies a Person (by fetching it), and that the person

View file

@ -1 +0,0 @@
pub mod create_or_update;

View file

@ -1,97 +0,0 @@
use crate::{
activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
protocol::activities::private_message::delete::DeletePrivateMessage,
};
use activitystreams_kinds::activity::DeleteType;
use lemmy_api_common::blocking;
use lemmy_apub_lib::{
data::Data,
object_id::ObjectId,
traits::{ActivityHandler, ActorType},
verify::verify_domains_match,
};
use lemmy_db_schema::{
source::{person::Person, private_message::PrivateMessage},
traits::Crud,
};
use lemmy_utils::LemmyError;
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
impl DeletePrivateMessage {
pub(in crate::activities::private_message) fn new(
actor: &ApubPerson,
pm: &PrivateMessage,
context: &LemmyContext,
) -> Result<DeletePrivateMessage, LemmyError> {
Ok(DeletePrivateMessage {
actor: ObjectId::new(actor.actor_id()),
to: [ObjectId::new(actor.actor_id())],
object: ObjectId::new(pm.ap_id.clone()),
kind: DeleteType::Delete,
id: generate_activity_id(
DeleteType::Delete,
&context.settings().get_protocol_and_hostname(),
)?,
unparsed: Default::default(),
})
}
#[tracing::instrument(skip_all)]
pub async fn send(
actor: &ApubPerson,
pm: &ApubPrivateMessage,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let delete = DeletePrivateMessage::new(actor, pm, context)?;
let delete_id = delete.id.clone();
let recipient_id = pm.recipient_id;
let recipient: ApubPerson =
blocking(context.pool(), move |conn| Person::read(conn, recipient_id))
.await??
.into();
let inbox = vec![recipient.shared_inbox_or_inbox_url()];
send_lemmy_activity(context, &delete, &delete_id, actor, inbox, true).await
}
}
#[async_trait::async_trait(?Send)]
impl ActivityHandler for DeletePrivateMessage {
type DataType = LemmyContext;
#[tracing::instrument(skip_all)]
async fn verify(
&self,
context: &Data<LemmyContext>,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
verify_activity(&self.id, self.actor.inner(), &context.settings())?;
verify_person(&self.actor, context, request_counter).await?;
verify_domains_match(self.actor.inner(), self.object.inner())?;
Ok(())
}
#[tracing::instrument(skip_all)]
async fn receive(
self,
context: &Data<LemmyContext>,
_request_counter: &mut i32,
) -> Result<(), LemmyError> {
let private_message = self.object.dereference_local(context).await?;
let deleted_private_message = blocking(context.pool(), move |conn| {
PrivateMessage::update_deleted(conn, private_message.id, true)
})
.await??;
send_pm_ws_message(
deleted_private_message.id,
UserOperationCrud::DeletePrivateMessage,
None,
context,
)
.await?;
Ok(())
}
}

View file

@ -1,3 +0,0 @@
pub mod create_or_update;
pub mod delete;
pub mod undo_delete;

View file

@ -1,97 +0,0 @@
use crate::{
activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
protocol::activities::private_message::{
delete::DeletePrivateMessage,
undo_delete::UndoDeletePrivateMessage,
},
};
use activitystreams_kinds::activity::UndoType;
use lemmy_api_common::blocking;
use lemmy_apub_lib::{
data::Data,
object_id::ObjectId,
traits::{ActivityHandler, ActorType},
verify::{verify_domains_match, verify_urls_match},
};
use lemmy_db_schema::{
source::{person::Person, private_message::PrivateMessage},
traits::Crud,
};
use lemmy_utils::LemmyError;
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
impl UndoDeletePrivateMessage {
#[tracing::instrument(skip_all)]
pub async fn send(
actor: &ApubPerson,
pm: &ApubPrivateMessage,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let recipient_id = pm.recipient_id;
let recipient: ApubPerson =
blocking(context.pool(), move |conn| Person::read(conn, recipient_id))
.await??
.into();
let object = DeletePrivateMessage::new(actor, pm, context)?;
let id = generate_activity_id(
UndoType::Undo,
&context.settings().get_protocol_and_hostname(),
)?;
let undo = UndoDeletePrivateMessage {
actor: ObjectId::new(actor.actor_id()),
to: [ObjectId::new(recipient.actor_id())],
object,
kind: UndoType::Undo,
id: id.clone(),
unparsed: Default::default(),
};
let inbox = vec![recipient.shared_inbox_or_inbox_url()];
send_lemmy_activity(context, &undo, &id, actor, inbox, true).await
}
}
#[async_trait::async_trait(?Send)]
impl ActivityHandler for UndoDeletePrivateMessage {
type DataType = LemmyContext;
#[tracing::instrument(skip_all)]
async fn verify(
&self,
context: &Data<LemmyContext>,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
verify_activity(&self.id, self.actor.inner(), &context.settings())?;
verify_person(&self.actor, context, request_counter).await?;
verify_urls_match(self.actor.inner(), self.object.actor.inner())?;
verify_domains_match(self.actor.inner(), self.object.object.inner())?;
self.object.verify(context, request_counter).await?;
Ok(())
}
#[tracing::instrument(skip_all)]
async fn receive(
self,
context: &Data<LemmyContext>,
_request_counter: &mut i32,
) -> Result<(), LemmyError> {
let ap_id = self.object.object.clone();
let private_message = ap_id.dereference_local(context).await?;
let deleted_private_message = blocking(context.pool(), move |conn| {
PrivateMessage::update_deleted(conn, private_message.id, false)
})
.await??;
send_pm_ws_message(
deleted_private_message.id,
UserOperationCrud::EditPrivateMessage,
None,
context,
)
.await?;
Ok(())
}
}

View file

@ -11,18 +11,17 @@ use crate::{
report::Report, report::Report,
update::UpdateCommunity, update::UpdateCommunity,
}, },
create_or_update::{comment::CreateOrUpdateComment, post::CreateOrUpdatePost}, create_or_update::{
comment::CreateOrUpdateComment,
post::CreateOrUpdatePost,
private_message::CreateOrUpdatePrivateMessage,
},
deletion::{delete::Delete, undo_delete::UndoDelete}, deletion::{delete::Delete, undo_delete::UndoDelete},
following::{ following::{
accept::AcceptFollowCommunity, accept::AcceptFollowCommunity,
follow::FollowCommunity, follow::FollowCommunity,
undo_follow::UndoFollowCommunity, undo_follow::UndoFollowCommunity,
}, },
private_message::{
create_or_update::CreateOrUpdatePrivateMessage,
delete::DeletePrivateMessage,
undo_delete::UndoDeletePrivateMessage,
},
voting::{undo_vote::UndoVote, vote::Vote}, voting::{undo_vote::UndoVote, vote::Vote},
}, },
objects::page::Page, objects::page::Page,
@ -61,8 +60,8 @@ pub enum PersonInboxActivities {
/// Some activities can also be sent from user to user, eg a comment with mentions /// Some activities can also be sent from user to user, eg a comment with mentions
AnnouncableActivities(AnnouncableActivities), AnnouncableActivities(AnnouncableActivities),
CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage), CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage),
DeletePrivateMessage(DeletePrivateMessage), Delete(Delete),
UndoDeletePrivateMessage(UndoDeletePrivateMessage), UndoDelete(UndoDelete),
AnnounceActivity(AnnounceActivity), AnnounceActivity(AnnounceActivity),
} }

View file

@ -84,10 +84,14 @@ pub(in crate::http) async fn receive_group_inbox(
let res = receive_activity(request, activity.clone(), activity_data, context).await?; let res = receive_activity(request, activity.clone(), activity_data, context).await?;
if let GroupInboxActivities::AnnouncableActivities(announcable) = activity { if let GroupInboxActivities::AnnouncableActivities(announcable) = activity {
let community = announcable.get_community(context, &mut 0).await?; // Ignore failures in get_community(). those happen because Delete/PrivateMessage is not in a
verify_person_in_community(&actor_id, &community, context, &mut 0).await?; // community, but looks identical to Delete/Post or Delete/Comment which are in a community.
if community.local { let community = announcable.get_community(context, &mut 0).await;
AnnounceActivity::send(*announcable, &community, context).await?; if let Ok(community) = community {
if community.local {
verify_person_in_community(&actor_id, &community, context, &mut 0).await?;
AnnounceActivity::send(*announcable, &community, context).await?;
}
} }
} }

View file

@ -1,5 +1,6 @@
pub mod comment; pub mod comment;
pub mod post; pub mod post;
pub mod private_message;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -7,7 +8,11 @@ mod tests {
context::WithContext, context::WithContext,
objects::tests::file_to_json_object, objects::tests::file_to_json_object,
protocol::{ protocol::{
activities::create_or_update::{comment::CreateOrUpdateComment, post::CreateOrUpdatePost}, activities::create_or_update::{
comment::CreateOrUpdateComment,
post::CreateOrUpdatePost,
private_message::CreateOrUpdatePrivateMessage,
},
tests::test_parse_lemmy_item, tests::test_parse_lemmy_item,
}, },
}; };
@ -26,6 +31,10 @@ mod tests {
"assets/lemmy/activities/create_or_update/create_note.json", "assets/lemmy/activities/create_or_update/create_note.json",
) )
.unwrap(); .unwrap();
test_parse_lemmy_item::<CreateOrUpdatePrivateMessage>(
"assets/lemmy/activities/create_or_update/create_private_message.json",
)
.unwrap();
file_to_json_object::<WithContext<CreateOrUpdateComment>>( file_to_json_object::<WithContext<CreateOrUpdateComment>>(
"assets/pleroma/activities/create_note.json", "assets/pleroma/activities/create_note.json",

View file

@ -1,7 +1,4 @@
use crate::{ use crate::{objects::person::ApubPerson, protocol::Unparsed};
objects::person::ApubPerson,
protocol::{objects::tombstone::Tombstone, Unparsed},
};
use activitystreams_kinds::activity::DeleteType; use activitystreams_kinds::activity::DeleteType;
use lemmy_apub_lib::object_id::ObjectId; use lemmy_apub_lib::object_id::ObjectId;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -15,7 +12,11 @@ pub struct Delete {
pub(crate) actor: ObjectId<ApubPerson>, pub(crate) actor: ObjectId<ApubPerson>,
#[serde(deserialize_with = "crate::deserialize_one_or_many")] #[serde(deserialize_with = "crate::deserialize_one_or_many")]
pub(crate) to: Vec<Url>, pub(crate) to: Vec<Url>,
pub(crate) object: Tombstone, pub(crate) object: IdOrNestedObject,
#[serde(deserialize_with = "crate::deserialize_one_or_many")]
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub(crate) cc: Vec<Url>,
#[serde(rename = "type")] #[serde(rename = "type")]
pub(crate) kind: DeleteType, pub(crate) kind: DeleteType,
/// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user /// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user
@ -25,3 +26,26 @@ pub struct Delete {
#[serde(flatten)] #[serde(flatten)]
pub(crate) unparsed: Unparsed, pub(crate) unparsed: Unparsed,
} }
/// Instead of a simple ID string as object, Mastodon sends a nested tombstone for some reason,
/// so we need to handle that as well.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub(crate) enum IdOrNestedObject {
Id(Url),
NestedObject(NestedObject),
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub(crate) struct NestedObject {
id: Url,
}
impl IdOrNestedObject {
pub(crate) fn id(&self) -> &Url {
match self {
IdOrNestedObject::Id(i) => i,
IdOrNestedObject::NestedObject(n) => &n.id,
}
}
}

View file

@ -3,13 +3,17 @@ pub mod undo_delete;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::protocol::{ use crate::{
activities::deletion::{delete::Delete, undo_delete::UndoDelete}, context::WithContext,
tests::test_parse_lemmy_item, objects::tests::file_to_json_object,
protocol::{
activities::deletion::{delete::Delete, undo_delete::UndoDelete},
tests::test_parse_lemmy_item,
},
}; };
#[actix_rt::test] #[actix_rt::test]
async fn test_parse_lemmy_deletion() { async fn test_parse_deletion() {
test_parse_lemmy_item::<Delete>("assets/lemmy/activities/deletion/remove_note.json").unwrap(); test_parse_lemmy_item::<Delete>("assets/lemmy/activities/deletion/remove_note.json").unwrap();
test_parse_lemmy_item::<Delete>("assets/lemmy/activities/deletion/delete_page.json").unwrap(); test_parse_lemmy_item::<Delete>("assets/lemmy/activities/deletion/delete_page.json").unwrap();
@ -17,5 +21,14 @@ mod tests {
.unwrap(); .unwrap();
test_parse_lemmy_item::<UndoDelete>("assets/lemmy/activities/deletion/undo_delete_page.json") test_parse_lemmy_item::<UndoDelete>("assets/lemmy/activities/deletion/undo_delete_page.json")
.unwrap(); .unwrap();
test_parse_lemmy_item::<Delete>("assets/lemmy/activities/deletion/delete_private_message.json")
.unwrap();
test_parse_lemmy_item::<UndoDelete>(
"assets/lemmy/activities/deletion/undo_delete_private_message.json",
)
.unwrap();
file_to_json_object::<WithContext<Delete>>("assets/pleroma/activities/delete.json").unwrap();
file_to_json_object::<WithContext<Delete>>("assets/mastodon/activities/delete.json").unwrap();
} }
} }

View file

@ -15,6 +15,8 @@ pub struct UndoDelete {
pub(crate) to: Vec<Url>, pub(crate) to: Vec<Url>,
pub(crate) object: Delete, pub(crate) object: Delete,
#[serde(deserialize_with = "crate::deserialize_one_or_many")] #[serde(deserialize_with = "crate::deserialize_one_or_many")]
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub(crate) cc: Vec<Url>, pub(crate) cc: Vec<Url>,
#[serde(rename = "type")] #[serde(rename = "type")]
pub(crate) kind: UndoType, pub(crate) kind: UndoType,

View file

@ -6,7 +6,6 @@ pub mod community;
pub mod create_or_update; pub mod create_or_update;
pub mod deletion; pub mod deletion;
pub mod following; pub mod following;
pub mod private_message;
pub mod voting; pub mod voting;
#[derive(Clone, Debug, Display, Deserialize, Serialize, PartialEq)] #[derive(Clone, Debug, Display, Deserialize, Serialize, PartialEq)]

View file

@ -1,22 +0,0 @@
use crate::{
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
protocol::Unparsed,
};
use activitystreams_kinds::activity::DeleteType;
use lemmy_apub_lib::object_id::ObjectId;
use serde::{Deserialize, Serialize};
use url::Url;
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DeletePrivateMessage {
pub(crate) actor: ObjectId<ApubPerson>,
#[serde(deserialize_with = "crate::deserialize_one")]
pub(crate) to: [ObjectId<ApubPerson>; 1],
pub(crate) object: ObjectId<ApubPrivateMessage>,
#[serde(rename = "type")]
pub(crate) kind: DeleteType,
pub(crate) id: Url,
#[serde(flatten)]
pub(crate) unparsed: Unparsed,
}

View file

@ -1,31 +0,0 @@
pub mod create_or_update;
pub mod delete;
pub mod undo_delete;
#[cfg(test)]
mod tests {
use crate::protocol::{
activities::private_message::{
create_or_update::CreateOrUpdatePrivateMessage,
delete::DeletePrivateMessage,
undo_delete::UndoDeletePrivateMessage,
},
tests::test_parse_lemmy_item,
};
#[actix_rt::test]
async fn test_parse_lemmy_private_message() {
test_parse_lemmy_item::<CreateOrUpdatePrivateMessage>(
"assets/lemmy/activities/private_message/create.json",
)
.unwrap();
test_parse_lemmy_item::<DeletePrivateMessage>(
"assets/lemmy/activities/private_message/delete.json",
)
.unwrap();
test_parse_lemmy_item::<UndoDeletePrivateMessage>(
"assets/lemmy/activities/private_message/undo_delete.json",
)
.unwrap();
}
}

View file

@ -1,22 +0,0 @@
use crate::{
objects::person::ApubPerson,
protocol::{activities::private_message::delete::DeletePrivateMessage, Unparsed},
};
use activitystreams_kinds::activity::UndoType;
use lemmy_apub_lib::object_id::ObjectId;
use serde::{Deserialize, Serialize};
use url::Url;
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UndoDeletePrivateMessage {
pub(crate) actor: ObjectId<ApubPerson>,
#[serde(deserialize_with = "crate::deserialize_one")]
pub(crate) to: [ObjectId<ApubPerson>; 1],
pub(crate) object: DeletePrivateMessage,
#[serde(rename = "type")]
pub(crate) kind: UndoType,
pub(crate) id: Url,
#[serde(flatten)]
pub(crate) unparsed: Unparsed,
}