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,
39 use lemmy_db_schema::{
41 comment::{Comment, CommentUpdateForm},
42 community::{Community, CommunityUpdateForm},
44 post::{Post, PostUpdateForm},
45 private_message::{PrivateMessage, PrivateMessageUpdateForm},
49 use lemmy_utils::error::LemmyError;
57 #[async_trait::async_trait]
58 impl SendActivity for DeletePost {
59 type Response = PostResponse;
61 async fn send_activity(
63 response: &Self::Response,
64 context: &Data<LemmyContext>,
65 ) -> Result<(), LemmyError> {
66 let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
67 let community = Community::read(&mut context.pool(), response.post_view.community.id).await?;
68 let deletable = DeletableObjects::Post(response.post_view.post.clone().into());
69 send_apub_delete_in_community(
70 local_user_view.person,
81 #[async_trait::async_trait]
82 impl SendActivity for RemovePost {
83 type Response = PostResponse;
85 async fn send_activity(
87 response: &Self::Response,
88 context: &Data<LemmyContext>,
89 ) -> Result<(), LemmyError> {
90 let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
91 let community = Community::read(&mut context.pool(), response.post_view.community.id).await?;
92 let deletable = DeletableObjects::Post(response.post_view.post.clone().into());
93 send_apub_delete_in_community(
94 local_user_view.person,
97 request.reason.clone().or_else(|| Some(String::new())),
105 #[async_trait::async_trait]
106 impl SendActivity for DeleteComment {
107 type Response = CommentResponse;
109 async fn send_activity(
111 response: &Self::Response,
112 context: &Data<LemmyContext>,
113 ) -> Result<(), LemmyError> {
114 let community_id = response.comment_view.community.id;
115 let community = Community::read(&mut context.pool(), community_id).await?;
116 let person = Person::read(&mut context.pool(), response.comment_view.creator.id).await?;
117 let deletable = DeletableObjects::Comment(response.comment_view.comment.clone().into());
118 send_apub_delete_in_community(person, community, deletable, None, request.deleted, context)
123 #[async_trait::async_trait]
124 impl SendActivity for RemoveComment {
125 type Response = CommentResponse;
127 async fn send_activity(
129 response: &Self::Response,
130 context: &Data<LemmyContext>,
131 ) -> Result<(), LemmyError> {
132 let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
133 let comment = Comment::read(&mut context.pool(), request.comment_id).await?;
135 Community::read(&mut 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(&mut 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(&mut 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(&mut context.pool(), recipient_id)
260 let deletable = DeletableObjects::PrivateMessage(pm.into());
261 let inbox = vec![recipient.shared_inbox_or_inbox()];
263 let delete = Delete::new(actor, deletable, recipient.id(), None, None, context)?;
264 send_lemmy_activity(context, delete, actor, inbox, true).await?;
266 let undo = UndoDelete::new(actor, deletable, recipient.id(), None, None, context)?;
267 send_lemmy_activity(context, undo, actor, inbox, true).await?;
272 pub enum DeletableObjects {
273 Community(ApubCommunity),
274 Comment(ApubComment),
276 PrivateMessage(ApubPrivateMessage),
279 impl DeletableObjects {
280 #[tracing::instrument(skip_all)]
281 pub(crate) async fn read_from_db(
283 context: &Data<LemmyContext>,
284 ) -> Result<DeletableObjects, LemmyError> {
285 if let Some(c) = ApubCommunity::read_from_id(ap_id.clone(), context).await? {
286 return Ok(DeletableObjects::Community(c));
288 if let Some(p) = ApubPost::read_from_id(ap_id.clone(), context).await? {
289 return Ok(DeletableObjects::Post(p));
291 if let Some(c) = ApubComment::read_from_id(ap_id.clone(), context).await? {
292 return Ok(DeletableObjects::Comment(c));
294 if let Some(p) = ApubPrivateMessage::read_from_id(ap_id.clone(), context).await? {
295 return Ok(DeletableObjects::PrivateMessage(p));
297 Err(diesel::NotFound.into())
300 pub(crate) fn id(&self) -> Url {
302 DeletableObjects::Community(c) => c.id(),
303 DeletableObjects::Comment(c) => c.ap_id.clone().into(),
304 DeletableObjects::Post(p) => p.ap_id.clone().into(),
305 DeletableObjects::PrivateMessage(p) => p.ap_id.clone().into(),
310 #[tracing::instrument(skip_all)]
311 pub(in crate::activities) async fn verify_delete_activity(
314 context: &Data<LemmyContext>,
315 ) -> Result<(), LemmyError> {
316 let object = DeletableObjects::read_from_db(activity.object.id(), context).await?;
318 DeletableObjects::Community(community) => {
319 verify_is_public(&activity.to, &[])?;
321 // can only do this check for local community, in remote case it would try to fetch the
322 // deleted community (which fails)
323 verify_person_in_community(&activity.actor, &community, context).await?;
325 // community deletion is always a mod (or admin) action
326 verify_mod_action(&activity.actor, activity.object.id(), community.id, context).await?;
328 DeletableObjects::Post(p) => {
329 verify_is_public(&activity.to, &[])?;
330 verify_delete_post_or_comment(
332 &p.ap_id.clone().into(),
333 &activity.community(context).await?,
339 DeletableObjects::Comment(c) => {
340 verify_is_public(&activity.to, &[])?;
341 verify_delete_post_or_comment(
343 &c.ap_id.clone().into(),
344 &activity.community(context).await?,
350 DeletableObjects::PrivateMessage(_) => {
351 verify_person(&activity.actor, context).await?;
352 verify_domains_match(activity.actor.inner(), activity.object.id())?;
358 #[tracing::instrument(skip_all)]
359 async fn verify_delete_post_or_comment(
360 actor: &ObjectId<ApubPerson>,
362 community: &ApubCommunity,
364 context: &Data<LemmyContext>,
365 ) -> Result<(), LemmyError> {
366 verify_person_in_community(actor, community, context).await?;
368 verify_mod_action(actor, object_id, community.id, context).await?;
370 // domain of post ap_id and post.creator ap_id are identical, so we just check the former
371 verify_domains_match(actor.inner(), object_id)?;
376 /// Write deletion or restoring of an object to the database, and send websocket message.
377 #[tracing::instrument(skip_all)]
378 async fn receive_delete_action(
380 actor: &ObjectId<ApubPerson>,
382 context: &Data<LemmyContext>,
383 ) -> Result<(), LemmyError> {
384 match DeletableObjects::read_from_db(object, context).await? {
385 DeletableObjects::Community(community) => {
387 let mod_: Person = actor.dereference(context).await?.deref().clone();
388 let object = DeletableObjects::Community(community.clone());
389 let c: Community = community.deref().deref().clone();
390 send_apub_delete_in_community(mod_, c, object, None, true, context).await?;
396 &CommunityUpdateForm::builder()
397 .deleted(Some(deleted))
402 DeletableObjects::Post(post) => {
403 if deleted != post.deleted {
407 &PostUpdateForm::builder().deleted(Some(deleted)).build(),
412 DeletableObjects::Comment(comment) => {
413 if deleted != comment.deleted {
417 &CommentUpdateForm::builder().deleted(Some(deleted)).build(),
422 DeletableObjects::PrivateMessage(pm) => {
423 PrivateMessage::update(
426 &PrivateMessageUpdateForm::builder()
427 .deleted(Some(deleted))