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