]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/block/undo_block_user.rs
Implement federated user following (fixes #752) (#2577)
[lemmy.git] / crates / apub / src / activities / block / undo_block_user.rs
1 use crate::{
2   activities::{
3     block::{generate_cc, SiteOrCommunity},
4     community::{announce::GetCommunity, send_activity_in_community},
5     generate_activity_id,
6     send_lemmy_activity,
7     verify_is_public,
8   },
9   activity_lists::AnnouncableActivities,
10   local_instance,
11   objects::{community::ApubCommunity, instance::remote_instance_inboxes, person::ApubPerson},
12   protocol::activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
13   ActorType,
14 };
15 use activitypub_federation::{
16   core::object_id::ObjectId,
17   data::Data,
18   traits::{ActivityHandler, Actor},
19   utils::verify_domains_match,
20 };
21 use activitystreams_kinds::{activity::UndoType, public};
22 use lemmy_db_schema::{
23   source::{
24     community::{CommunityPersonBan, CommunityPersonBanForm},
25     moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm},
26     person::{Person, PersonUpdateForm},
27   },
28   traits::{Bannable, Crud},
29 };
30 use lemmy_utils::error::LemmyError;
31 use lemmy_websocket::LemmyContext;
32 use url::Url;
33
34 impl UndoBlockUser {
35   #[tracing::instrument(skip_all)]
36   pub async fn send(
37     target: &SiteOrCommunity,
38     user: &ApubPerson,
39     mod_: &ApubPerson,
40     reason: Option<String>,
41     context: &LemmyContext,
42   ) -> Result<(), LemmyError> {
43     let block = BlockUser::new(target, user, mod_, None, reason, None, context).await?;
44
45     let id = generate_activity_id(
46       UndoType::Undo,
47       &context.settings().get_protocol_and_hostname(),
48     )?;
49     let undo = UndoBlockUser {
50       actor: ObjectId::new(mod_.actor_id()),
51       to: vec![public()],
52       object: block,
53       cc: generate_cc(target, context.pool()).await?,
54       kind: UndoType::Undo,
55       id: id.clone(),
56     };
57
58     let mut inboxes = vec![user.shared_inbox_or_inbox()];
59     match target {
60       SiteOrCommunity::Site(_) => {
61         inboxes.append(&mut remote_instance_inboxes(context.pool()).await?);
62         send_lemmy_activity(context, undo, mod_, inboxes, false).await
63       }
64       SiteOrCommunity::Community(c) => {
65         let activity = AnnouncableActivities::UndoBlockUser(undo);
66         send_activity_in_community(activity, mod_, c, inboxes, true, context).await
67       }
68     }
69   }
70 }
71
72 #[async_trait::async_trait(?Send)]
73 impl ActivityHandler for UndoBlockUser {
74   type DataType = LemmyContext;
75   type Error = LemmyError;
76
77   fn id(&self) -> &Url {
78     &self.id
79   }
80
81   fn actor(&self) -> &Url {
82     self.actor.inner()
83   }
84
85   #[tracing::instrument(skip_all)]
86   async fn verify(
87     &self,
88     context: &Data<LemmyContext>,
89     request_counter: &mut i32,
90   ) -> Result<(), LemmyError> {
91     verify_is_public(&self.to, &self.cc)?;
92     verify_domains_match(self.actor.inner(), self.object.actor.inner())?;
93     self.object.verify(context, request_counter).await?;
94     Ok(())
95   }
96
97   #[tracing::instrument(skip_all)]
98   async fn receive(
99     self,
100     context: &Data<LemmyContext>,
101     request_counter: &mut i32,
102   ) -> Result<(), LemmyError> {
103     let instance = local_instance(context).await;
104     let expires = self.object.expires.map(|u| u.naive_local());
105     let mod_person = self
106       .actor
107       .dereference(context, instance, request_counter)
108       .await?;
109     let blocked_person = self
110       .object
111       .object
112       .dereference(context, instance, request_counter)
113       .await?;
114     match self
115       .object
116       .target
117       .dereference(context, instance, request_counter)
118       .await?
119     {
120       SiteOrCommunity::Site(_site) => {
121         let blocked_person = Person::update(
122           context.pool(),
123           blocked_person.id,
124           &PersonUpdateForm::builder()
125             .banned(Some(false))
126             .ban_expires(Some(expires))
127             .build(),
128         )
129         .await?;
130
131         // write mod log
132         let form = ModBanForm {
133           mod_person_id: mod_person.id,
134           other_person_id: blocked_person.id,
135           reason: self.object.summary,
136           banned: Some(false),
137           expires,
138         };
139         ModBan::create(context.pool(), &form).await?;
140       }
141       SiteOrCommunity::Community(community) => {
142         let community_user_ban_form = CommunityPersonBanForm {
143           community_id: community.id,
144           person_id: blocked_person.id,
145           expires: None,
146         };
147         CommunityPersonBan::unban(context.pool(), &community_user_ban_form).await?;
148
149         // write to mod log
150         let form = ModBanFromCommunityForm {
151           mod_person_id: mod_person.id,
152           other_person_id: blocked_person.id,
153           community_id: community.id,
154           reason: self.object.summary,
155           banned: Some(false),
156           expires,
157         };
158         ModBanFromCommunity::create(context.pool(), &form).await?;
159       }
160     }
161
162     Ok(())
163   }
164 }
165
166 #[async_trait::async_trait(?Send)]
167 impl GetCommunity for UndoBlockUser {
168   #[tracing::instrument(skip_all)]
169   async fn get_community(
170     &self,
171     context: &LemmyContext,
172     request_counter: &mut i32,
173   ) -> Result<ApubCommunity, LemmyError> {
174     self.object.get_community(context, request_counter).await
175   }
176 }