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(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(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(context.pool(), community_id).await?;
116 let person = Person::read(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(context.pool(), request.comment_id).await?;
134 let community = Community::read(context.pool(), response.comment_view.community.id).await?;
135 let deletable = DeletableObjects::Comment(comment.into());
136 send_apub_delete_in_community(
137 local_user_view.person,
140 request.reason.clone().or_else(|| Some(String::new())),
148 #[async_trait::async_trait]
149 impl SendActivity for DeletePrivateMessage {
150 type Response = PrivateMessageResponse;
152 async fn send_activity(
154 response: &Self::Response,
155 context: &Data<LemmyContext>,
156 ) -> Result<(), LemmyError> {
157 let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
158 send_apub_delete_private_message(
159 &local_user_view.person.into(),
160 response.private_message_view.private_message.clone(),
168 #[async_trait::async_trait]
169 impl SendActivity for DeleteCommunity {
170 type Response = CommunityResponse;
172 async fn send_activity(
174 _response: &Self::Response,
175 context: &Data<LemmyContext>,
176 ) -> Result<(), LemmyError> {
177 let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
178 let community = Community::read(context.pool(), request.community_id).await?;
179 let deletable = DeletableObjects::Community(community.clone().into());
180 send_apub_delete_in_community(
181 local_user_view.person,
192 #[async_trait::async_trait]
193 impl SendActivity for RemoveCommunity {
194 type Response = CommunityResponse;
196 async fn send_activity(
198 _response: &Self::Response,
199 context: &Data<LemmyContext>,
200 ) -> Result<(), LemmyError> {
201 let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
202 let community = Community::read(context.pool(), request.community_id).await?;
203 let deletable = DeletableObjects::Community(community.clone().into());
204 send_apub_delete_in_community(
205 local_user_view.person,
208 request.reason.clone().or_else(|| Some(String::new())),
216 /// Parameter `reason` being set indicates that this is a removal by a mod. If its unset, this
217 /// action was done by a normal user.
218 #[tracing::instrument(skip_all)]
219 async fn send_apub_delete_in_community(
221 community: Community,
222 object: DeletableObjects,
223 reason: Option<String>,
225 context: &Data<LemmyContext>,
226 ) -> Result<(), LemmyError> {
227 let actor = ApubPerson::from(actor);
228 let is_mod_action = reason.is_some();
229 let activity = if deleted {
230 let delete = Delete::new(&actor, object, public(), Some(&community), reason, context)?;
231 AnnouncableActivities::Delete(delete)
233 let undo = UndoDelete::new(&actor, object, public(), Some(&community), reason, context)?;
234 AnnouncableActivities::UndoDelete(undo)
236 send_activity_in_community(
247 #[tracing::instrument(skip_all)]
248 async fn send_apub_delete_private_message(
252 context: &Data<LemmyContext>,
253 ) -> Result<(), LemmyError> {
254 let recipient_id = pm.recipient_id;
255 let recipient: ApubPerson = Person::read(context.pool(), recipient_id).await?.into();
257 let deletable = DeletableObjects::PrivateMessage(pm.into());
258 let inbox = vec![recipient.shared_inbox_or_inbox()];
260 let delete = Delete::new(actor, deletable, recipient.id(), None, None, context)?;
261 send_lemmy_activity(context, delete, actor, inbox, true).await?;
263 let undo = UndoDelete::new(actor, deletable, recipient.id(), None, None, context)?;
264 send_lemmy_activity(context, undo, actor, inbox, true).await?;
269 pub enum DeletableObjects {
270 Community(ApubCommunity),
271 Comment(ApubComment),
273 PrivateMessage(ApubPrivateMessage),
276 impl DeletableObjects {
277 #[tracing::instrument(skip_all)]
278 pub(crate) async fn read_from_db(
280 context: &Data<LemmyContext>,
281 ) -> Result<DeletableObjects, LemmyError> {
282 if let Some(c) = ApubCommunity::read_from_id(ap_id.clone(), context).await? {
283 return Ok(DeletableObjects::Community(c));
285 if let Some(p) = ApubPost::read_from_id(ap_id.clone(), context).await? {
286 return Ok(DeletableObjects::Post(p));
288 if let Some(c) = ApubComment::read_from_id(ap_id.clone(), context).await? {
289 return Ok(DeletableObjects::Comment(c));
291 if let Some(p) = ApubPrivateMessage::read_from_id(ap_id.clone(), context).await? {
292 return Ok(DeletableObjects::PrivateMessage(p));
294 Err(diesel::NotFound.into())
297 pub(crate) fn id(&self) -> Url {
299 DeletableObjects::Community(c) => c.id(),
300 DeletableObjects::Comment(c) => c.ap_id.clone().into(),
301 DeletableObjects::Post(p) => p.ap_id.clone().into(),
302 DeletableObjects::PrivateMessage(p) => p.ap_id.clone().into(),
307 #[tracing::instrument(skip_all)]
308 pub(in crate::activities) async fn verify_delete_activity(
311 context: &Data<LemmyContext>,
312 ) -> Result<(), LemmyError> {
313 let object = DeletableObjects::read_from_db(activity.object.id(), context).await?;
315 DeletableObjects::Community(community) => {
316 verify_is_public(&activity.to, &[])?;
318 // can only do this check for local community, in remote case it would try to fetch the
319 // deleted community (which fails)
320 verify_person_in_community(&activity.actor, &community, context).await?;
322 // community deletion is always a mod (or admin) action
323 verify_mod_action(&activity.actor, activity.object.id(), community.id, context).await?;
325 DeletableObjects::Post(p) => {
326 verify_is_public(&activity.to, &[])?;
327 verify_delete_post_or_comment(
329 &p.ap_id.clone().into(),
330 &activity.community(context).await?,
336 DeletableObjects::Comment(c) => {
337 verify_is_public(&activity.to, &[])?;
338 verify_delete_post_or_comment(
340 &c.ap_id.clone().into(),
341 &activity.community(context).await?,
347 DeletableObjects::PrivateMessage(_) => {
348 verify_person(&activity.actor, context).await?;
349 verify_domains_match(activity.actor.inner(), activity.object.id())?;
355 #[tracing::instrument(skip_all)]
356 async fn verify_delete_post_or_comment(
357 actor: &ObjectId<ApubPerson>,
359 community: &ApubCommunity,
361 context: &Data<LemmyContext>,
362 ) -> Result<(), LemmyError> {
363 verify_person_in_community(actor, community, context).await?;
365 verify_mod_action(actor, object_id, community.id, context).await?;
367 // domain of post ap_id and post.creator ap_id are identical, so we just check the former
368 verify_domains_match(actor.inner(), object_id)?;
373 /// Write deletion or restoring of an object to the database, and send websocket message.
374 #[tracing::instrument(skip_all)]
375 async fn receive_delete_action(
377 actor: &ObjectId<ApubPerson>,
379 context: &Data<LemmyContext>,
380 ) -> Result<(), LemmyError> {
381 match DeletableObjects::read_from_db(object, context).await? {
382 DeletableObjects::Community(community) => {
384 let mod_: Person = actor.dereference(context).await?.deref().clone();
385 let object = DeletableObjects::Community(community.clone());
386 let c: Community = community.deref().deref().clone();
387 send_apub_delete_in_community(mod_, c, object, None, true, context).await?;
393 &CommunityUpdateForm::builder()
394 .deleted(Some(deleted))
399 DeletableObjects::Post(post) => {
400 if deleted != post.deleted {
404 &PostUpdateForm::builder().deleted(Some(deleted)).build(),
409 DeletableObjects::Comment(comment) => {
410 if deleted != comment.deleted {
414 &CommentUpdateForm::builder().deleted(Some(deleted)).build(),
419 DeletableObjects::PrivateMessage(pm) => {
420 PrivateMessage::update(
423 &PrivateMessageUpdateForm::builder()
424 .deleted(Some(deleted))