3 community::send_activity_in_community,
8 verify_person_in_community,
10 activity_lists::AnnouncableActivities,
13 community::ApubCommunity,
16 private_message::ApubPrivateMessage,
19 activities::deletion::{delete::Delete, undo_delete::UndoDelete},
24 use activitypub_federation::{
26 fetch::object_id::ObjectId,
28 protocol::verification::verify_domains_match,
29 traits::{Actor, Object},
31 use lemmy_api_common::{
32 comment::{CommentResponse, DeleteComment, RemoveComment},
33 community::{CommunityResponse, DeleteCommunity, RemoveCommunity},
34 context::LemmyContext,
35 post::{DeletePost, PostResponse, RemovePost},
36 private_message::{DeletePrivateMessage, PrivateMessageResponse},
37 utils::local_user_view_from_jwt,
38 websocket::UserOperationCrud,
40 use lemmy_db_schema::{
42 comment::{Comment, CommentUpdateForm},
43 community::{Community, CommunityUpdateForm},
45 post::{Post, PostUpdateForm},
46 private_message::{PrivateMessage, PrivateMessageUpdateForm},
50 use lemmy_utils::error::LemmyError;
58 #[async_trait::async_trait]
59 impl SendActivity for DeletePost {
60 type Response = PostResponse;
62 async fn send_activity(
64 response: &Self::Response,
65 context: &Data<LemmyContext>,
66 ) -> Result<(), LemmyError> {
67 let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
68 let community = Community::read(context.pool(), response.post_view.community.id).await?;
69 let deletable = DeletableObjects::Post(response.post_view.post.clone().into());
70 send_apub_delete_in_community(
71 local_user_view.person,
82 #[async_trait::async_trait]
83 impl SendActivity for RemovePost {
84 type Response = PostResponse;
86 async fn send_activity(
88 response: &Self::Response,
89 context: &Data<LemmyContext>,
90 ) -> Result<(), LemmyError> {
91 let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
92 let community = Community::read(context.pool(), response.post_view.community.id).await?;
93 let deletable = DeletableObjects::Post(response.post_view.post.clone().into());
94 send_apub_delete_in_community(
95 local_user_view.person,
98 request.reason.clone().or_else(|| Some(String::new())),
106 #[async_trait::async_trait]
107 impl SendActivity for DeleteComment {
108 type Response = CommentResponse;
110 async fn send_activity(
112 response: &Self::Response,
113 context: &Data<LemmyContext>,
114 ) -> Result<(), LemmyError> {
115 let community_id = response.comment_view.community.id;
116 let community = Community::read(context.pool(), community_id).await?;
117 let person = Person::read(context.pool(), response.comment_view.creator.id).await?;
118 let deletable = DeletableObjects::Comment(response.comment_view.comment.clone().into());
119 send_apub_delete_in_community(person, community, deletable, None, request.deleted, context)
124 #[async_trait::async_trait]
125 impl SendActivity for RemoveComment {
126 type Response = CommentResponse;
128 async fn send_activity(
130 response: &Self::Response,
131 context: &Data<LemmyContext>,
132 ) -> Result<(), LemmyError> {
133 let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
134 let comment = Comment::read(context.pool(), request.comment_id).await?;
135 let community = Community::read(context.pool(), response.comment_view.community.id).await?;
136 let deletable = DeletableObjects::Comment(comment.into());
137 send_apub_delete_in_community(
138 local_user_view.person,
141 request.reason.clone().or_else(|| Some(String::new())),
149 #[async_trait::async_trait]
150 impl SendActivity for DeletePrivateMessage {
151 type Response = PrivateMessageResponse;
153 async fn send_activity(
155 response: &Self::Response,
156 context: &Data<LemmyContext>,
157 ) -> Result<(), LemmyError> {
158 let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
159 send_apub_delete_private_message(
160 &local_user_view.person.into(),
161 response.private_message_view.private_message.clone(),
169 #[async_trait::async_trait]
170 impl SendActivity for DeleteCommunity {
171 type Response = CommunityResponse;
173 async fn send_activity(
175 _response: &Self::Response,
176 context: &Data<LemmyContext>,
177 ) -> Result<(), LemmyError> {
178 let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
179 let community = Community::read(context.pool(), request.community_id).await?;
180 let deletable = DeletableObjects::Community(community.clone().into());
181 send_apub_delete_in_community(
182 local_user_view.person,
193 #[async_trait::async_trait]
194 impl SendActivity for RemoveCommunity {
195 type Response = CommunityResponse;
197 async fn send_activity(
199 _response: &Self::Response,
200 context: &Data<LemmyContext>,
201 ) -> Result<(), LemmyError> {
202 let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
203 let community = Community::read(context.pool(), request.community_id).await?;
204 let deletable = DeletableObjects::Community(community.clone().into());
205 send_apub_delete_in_community(
206 local_user_view.person,
209 request.reason.clone().or_else(|| Some(String::new())),
217 /// Parameter `reason` being set indicates that this is a removal by a mod. If its unset, this
218 /// action was done by a normal user.
219 #[tracing::instrument(skip_all)]
220 async fn send_apub_delete_in_community(
222 community: Community,
223 object: DeletableObjects,
224 reason: Option<String>,
226 context: &Data<LemmyContext>,
227 ) -> Result<(), LemmyError> {
228 let actor = ApubPerson::from(actor);
229 let is_mod_action = reason.is_some();
230 let activity = if deleted {
231 let delete = Delete::new(&actor, object, public(), Some(&community), reason, context)?;
232 AnnouncableActivities::Delete(delete)
234 let undo = UndoDelete::new(&actor, object, public(), Some(&community), reason, context)?;
235 AnnouncableActivities::UndoDelete(undo)
237 send_activity_in_community(
248 #[tracing::instrument(skip_all)]
249 async fn send_apub_delete_private_message(
253 context: &Data<LemmyContext>,
254 ) -> Result<(), LemmyError> {
255 let recipient_id = pm.recipient_id;
256 let recipient: ApubPerson = Person::read(context.pool(), recipient_id).await?.into();
258 let deletable = DeletableObjects::PrivateMessage(pm.into());
259 let inbox = vec![recipient.shared_inbox_or_inbox()];
261 let delete = Delete::new(actor, deletable, recipient.id(), None, None, context)?;
262 send_lemmy_activity(context, delete, actor, inbox, true).await?;
264 let undo = UndoDelete::new(actor, deletable, recipient.id(), None, None, context)?;
265 send_lemmy_activity(context, undo, actor, inbox, true).await?;
270 pub enum DeletableObjects {
271 Community(ApubCommunity),
272 Comment(ApubComment),
274 PrivateMessage(ApubPrivateMessage),
277 impl DeletableObjects {
278 #[tracing::instrument(skip_all)]
279 pub(crate) async fn read_from_db(
281 context: &Data<LemmyContext>,
282 ) -> Result<DeletableObjects, LemmyError> {
283 if let Some(c) = ApubCommunity::read_from_id(ap_id.clone(), context).await? {
284 return Ok(DeletableObjects::Community(c));
286 if let Some(p) = ApubPost::read_from_id(ap_id.clone(), context).await? {
287 return Ok(DeletableObjects::Post(p));
289 if let Some(c) = ApubComment::read_from_id(ap_id.clone(), context).await? {
290 return Ok(DeletableObjects::Comment(c));
292 if let Some(p) = ApubPrivateMessage::read_from_id(ap_id.clone(), context).await? {
293 return Ok(DeletableObjects::PrivateMessage(p));
295 Err(diesel::NotFound.into())
298 pub(crate) fn id(&self) -> Url {
300 DeletableObjects::Community(c) => c.id(),
301 DeletableObjects::Comment(c) => c.ap_id.clone().into(),
302 DeletableObjects::Post(p) => p.ap_id.clone().into(),
303 DeletableObjects::PrivateMessage(p) => p.ap_id.clone().into(),
308 #[tracing::instrument(skip_all)]
309 pub(in crate::activities) async fn verify_delete_activity(
312 context: &Data<LemmyContext>,
313 ) -> Result<(), LemmyError> {
314 let object = DeletableObjects::read_from_db(activity.object.id(), context).await?;
316 DeletableObjects::Community(community) => {
317 verify_is_public(&activity.to, &[])?;
319 // can only do this check for local community, in remote case it would try to fetch the
320 // deleted community (which fails)
321 verify_person_in_community(&activity.actor, &community, context).await?;
323 // community deletion is always a mod (or admin) action
324 verify_mod_action(&activity.actor, activity.object.id(), community.id, context).await?;
326 DeletableObjects::Post(p) => {
327 verify_is_public(&activity.to, &[])?;
328 verify_delete_post_or_comment(
330 &p.ap_id.clone().into(),
331 &activity.community(context).await?,
337 DeletableObjects::Comment(c) => {
338 verify_is_public(&activity.to, &[])?;
339 verify_delete_post_or_comment(
341 &c.ap_id.clone().into(),
342 &activity.community(context).await?,
348 DeletableObjects::PrivateMessage(_) => {
349 verify_person(&activity.actor, context).await?;
350 verify_domains_match(activity.actor.inner(), activity.object.id())?;
356 #[tracing::instrument(skip_all)]
357 async fn verify_delete_post_or_comment(
358 actor: &ObjectId<ApubPerson>,
360 community: &ApubCommunity,
362 context: &Data<LemmyContext>,
363 ) -> Result<(), LemmyError> {
364 verify_person_in_community(actor, community, context).await?;
366 verify_mod_action(actor, object_id, community.id, context).await?;
368 // domain of post ap_id and post.creator ap_id are identical, so we just check the former
369 verify_domains_match(actor.inner(), object_id)?;
374 /// Write deletion or restoring of an object to the database, and send websocket message.
375 #[tracing::instrument(skip_all)]
376 async fn receive_delete_action(
378 actor: &ObjectId<ApubPerson>,
380 context: &Data<LemmyContext>,
381 ) -> Result<(), LemmyError> {
382 match DeletableObjects::read_from_db(object, context).await? {
383 DeletableObjects::Community(community) => {
385 let mod_: Person = actor.dereference(context).await?.deref().clone();
386 let object = DeletableObjects::Community(community.clone());
387 let c: Community = community.deref().deref().clone();
388 send_apub_delete_in_community(mod_, c, object, None, true, context).await?;
391 let community = Community::update(
394 &CommunityUpdateForm::builder()
395 .deleted(Some(deleted))
400 .send_community_ws_message(
401 &UserOperationCrud::DeleteCommunity,
408 DeletableObjects::Post(post) => {
409 if deleted != post.deleted {
410 let deleted_post = Post::update(
413 &PostUpdateForm::builder().deleted(Some(deleted)).build(),
417 .send_post_ws_message(&UserOperationCrud::DeletePost, deleted_post.id, None, None)
421 DeletableObjects::Comment(comment) => {
422 if deleted != comment.deleted {
423 let deleted_comment = Comment::update(
426 &CommentUpdateForm::builder().deleted(Some(deleted)).build(),
430 .send_comment_ws_message_simple(&UserOperationCrud::DeleteComment, deleted_comment.id)
434 DeletableObjects::PrivateMessage(pm) => {
435 let deleted_private_message = PrivateMessage::update(
438 &PrivateMessageUpdateForm::builder()
439 .deleted(Some(deleted))
446 &UserOperationCrud::DeletePrivateMessage,
447 deleted_private_message.id,