]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/block/block_user.rs
Dropping default on pending column.
[lemmy.git] / crates / apub / src / activities / block / block_user.rs
1 use crate::{
2   activities::{
3     block::{generate_cc, generate_instance_inboxes, SiteOrCommunity},
4     community::{announce::GetCommunity, send_activity_in_community},
5     generate_activity_id,
6     send_lemmy_activity,
7     verify_activity,
8     verify_is_public,
9     verify_mod_action,
10     verify_person_in_community,
11   },
12   activity_lists::AnnouncableActivities,
13   objects::{community::ApubCommunity, person::ApubPerson},
14   protocol::activities::block::block_user::BlockUser,
15 };
16 use activitystreams_kinds::{activity::BlockType, public};
17 use anyhow::anyhow;
18 use chrono::NaiveDateTime;
19 use lemmy_api_common::utils::{blocking, remove_user_data, remove_user_data_in_community};
20 use lemmy_apub_lib::{
21   data::Data,
22   object_id::ObjectId,
23   traits::{ActivityHandler, ActorType},
24   verify::verify_domains_match,
25 };
26 use lemmy_db_schema::{
27   source::{
28     community::{
29       CommunityFollower,
30       CommunityFollowerForm,
31       CommunityPersonBan,
32       CommunityPersonBanForm,
33     },
34     moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm},
35     person::Person,
36   },
37   traits::{Bannable, Crud, Followable},
38 };
39 use lemmy_utils::{settings::structs::Settings, utils::convert_datetime, LemmyError};
40 use lemmy_websocket::LemmyContext;
41
42 impl BlockUser {
43   pub(in crate::activities::block) async fn new(
44     target: &SiteOrCommunity,
45     user: &ApubPerson,
46     mod_: &ApubPerson,
47     remove_data: Option<bool>,
48     reason: Option<String>,
49     expires: Option<NaiveDateTime>,
50     context: &LemmyContext,
51   ) -> Result<BlockUser, LemmyError> {
52     Ok(BlockUser {
53       actor: ObjectId::new(mod_.actor_id()),
54       to: vec![public()],
55       object: ObjectId::new(user.actor_id()),
56       cc: generate_cc(target, context.pool()).await?,
57       target: target.id(),
58       kind: BlockType::Block,
59       remove_data,
60       summary: reason,
61       id: generate_activity_id(
62         BlockType::Block,
63         &context.settings().get_protocol_and_hostname(),
64       )?,
65       expires: expires.map(convert_datetime),
66       unparsed: Default::default(),
67     })
68   }
69
70   #[tracing::instrument(skip_all)]
71   pub async fn send(
72     target: &SiteOrCommunity,
73     user: &ApubPerson,
74     mod_: &ApubPerson,
75     remove_data: bool,
76     reason: Option<String>,
77     expires: Option<NaiveDateTime>,
78     context: &LemmyContext,
79   ) -> Result<(), LemmyError> {
80     let block = BlockUser::new(
81       target,
82       user,
83       mod_,
84       Some(remove_data),
85       reason,
86       expires,
87       context,
88     )
89     .await?;
90     let block_id = block.id.clone();
91
92     match target {
93       SiteOrCommunity::Site(_) => {
94         let inboxes = generate_instance_inboxes(user, context.pool()).await?;
95         send_lemmy_activity(context, &block, &block_id, mod_, inboxes, false).await
96       }
97       SiteOrCommunity::Community(c) => {
98         let activity = AnnouncableActivities::BlockUser(block);
99         let inboxes = vec![user.shared_inbox_or_inbox_url()];
100         send_activity_in_community(activity, &block_id, mod_, c, inboxes, context).await
101       }
102     }
103   }
104 }
105
106 #[async_trait::async_trait(?Send)]
107 impl ActivityHandler for BlockUser {
108   type DataType = LemmyContext;
109
110   #[tracing::instrument(skip_all)]
111   async fn verify(
112     &self,
113     context: &Data<LemmyContext>,
114     request_counter: &mut i32,
115   ) -> Result<(), LemmyError> {
116     verify_is_public(&self.to, &self.cc)?;
117     verify_activity(&self.id, self.actor.inner(), &context.settings())?;
118     match self
119       .target
120       .dereference(context, context.client(), request_counter)
121       .await?
122     {
123       SiteOrCommunity::Site(site) => {
124         let domain = self.object.inner().domain().expect("url needs domain");
125         if Settings::get().hostname == domain {
126           return Err(
127             anyhow!("Site bans from remote instance can't affect user's home instance").into(),
128           );
129         }
130         // site ban can only target a user who is on the same instance as the actor (admin)
131         verify_domains_match(&site.actor_id(), self.actor.inner())?;
132         verify_domains_match(&site.actor_id(), self.object.inner())?;
133       }
134       SiteOrCommunity::Community(community) => {
135         verify_person_in_community(&self.actor, &community, context, request_counter).await?;
136         verify_mod_action(
137           &self.actor,
138           self.object.inner(),
139           &community,
140           context,
141           request_counter,
142         )
143         .await?;
144       }
145     }
146     Ok(())
147   }
148
149   #[tracing::instrument(skip_all)]
150   async fn receive(
151     self,
152     context: &Data<LemmyContext>,
153     request_counter: &mut i32,
154   ) -> Result<(), LemmyError> {
155     let expires = self.expires.map(|u| u.naive_local());
156     let mod_person = self
157       .actor
158       .dereference(context, context.client(), request_counter)
159       .await?;
160     let blocked_person = self
161       .object
162       .dereference(context, context.client(), request_counter)
163       .await?;
164     let target = self
165       .target
166       .dereference(context, context.client(), request_counter)
167       .await?;
168     match target {
169       SiteOrCommunity::Site(_site) => {
170         let blocked_person = blocking(context.pool(), move |conn| {
171           Person::ban_person(conn, blocked_person.id, true, expires)
172         })
173         .await??;
174         if self.remove_data.unwrap_or(false) {
175           remove_user_data(blocked_person.id, context.pool()).await?;
176         }
177
178         // write mod log
179         let form = ModBanForm {
180           mod_person_id: mod_person.id,
181           other_person_id: blocked_person.id,
182           reason: self.summary,
183           banned: Some(true),
184           expires,
185         };
186         blocking(context.pool(), move |conn| ModBan::create(conn, &form)).await??;
187       }
188       SiteOrCommunity::Community(community) => {
189         let community_user_ban_form = CommunityPersonBanForm {
190           community_id: community.id,
191           person_id: blocked_person.id,
192           expires: Some(expires),
193         };
194         blocking(context.pool(), move |conn| {
195           CommunityPersonBan::ban(conn, &community_user_ban_form)
196         })
197         .await??;
198
199         // Also unsubscribe them from the community, if they are subscribed
200         let community_follower_form = CommunityFollowerForm {
201           community_id: community.id,
202           person_id: blocked_person.id,
203           pending: false,
204         };
205         blocking(context.pool(), move |conn: &'_ _| {
206           CommunityFollower::unfollow(conn, &community_follower_form)
207         })
208         .await?
209         .ok();
210
211         if self.remove_data.unwrap_or(false) {
212           remove_user_data_in_community(community.id, blocked_person.id, context.pool()).await?;
213         }
214
215         // write to mod log
216         let form = ModBanFromCommunityForm {
217           mod_person_id: mod_person.id,
218           other_person_id: blocked_person.id,
219           community_id: community.id,
220           reason: self.summary,
221           banned: Some(true),
222           expires,
223         };
224         blocking(context.pool(), move |conn| {
225           ModBanFromCommunity::create(conn, &form)
226         })
227         .await??;
228       }
229     }
230
231     Ok(())
232   }
233 }
234
235 #[async_trait::async_trait(?Send)]
236 impl GetCommunity for BlockUser {
237   #[tracing::instrument(skip_all)]
238   async fn get_community(
239     &self,
240     context: &LemmyContext,
241     request_counter: &mut i32,
242   ) -> Result<ApubCommunity, LemmyError> {
243     let target = self
244       .target
245       .dereference(context, context.client(), request_counter)
246       .await?;
247     match target {
248       SiteOrCommunity::Community(c) => Ok(c),
249       SiteOrCommunity::Site(_) => Err(anyhow!("Calling get_community() on site activity").into()),
250     }
251   }
252 }