]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/block/mod.rs
Merge pull request #2593 from LemmyNet/refactor-notifications
[lemmy.git] / crates / apub / src / activities / block / mod.rs
1 use crate::{
2   objects::{community::ApubCommunity, instance::ApubSite, person::ApubPerson},
3   protocol::{
4     activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
5     objects::{group::Group, instance::Instance},
6   },
7   ActorType,
8   SendActivity,
9 };
10 use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject};
11 use chrono::NaiveDateTime;
12 use lemmy_api_common::{
13   community::{BanFromCommunity, BanFromCommunityResponse},
14   context::LemmyContext,
15   person::{BanPerson, BanPersonResponse},
16   utils::get_local_user_view_from_jwt,
17 };
18 use lemmy_db_schema::{
19   source::{community::Community, person::Person, site::Site},
20   traits::Crud,
21   utils::DbPool,
22 };
23 use lemmy_db_views::structs::SiteView;
24 use lemmy_utils::{error::LemmyError, utils::naive_from_unix};
25 use serde::Deserialize;
26 use url::Url;
27
28 pub mod block_user;
29 pub mod undo_block_user;
30
31 #[derive(Clone, Debug)]
32 pub enum SiteOrCommunity {
33   Site(ApubSite),
34   Community(ApubCommunity),
35 }
36
37 #[derive(Deserialize)]
38 #[serde(untagged)]
39 pub enum InstanceOrGroup {
40   Instance(Instance),
41   Group(Group),
42 }
43
44 #[async_trait::async_trait(?Send)]
45 impl ApubObject for SiteOrCommunity {
46   type DataType = LemmyContext;
47   type ApubType = InstanceOrGroup;
48   type DbType = ();
49   type Error = LemmyError;
50
51   #[tracing::instrument(skip_all)]
52   fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
53     Some(match self {
54       SiteOrCommunity::Site(i) => i.last_refreshed_at,
55       SiteOrCommunity::Community(c) => c.last_refreshed_at,
56     })
57   }
58
59   #[tracing::instrument(skip_all)]
60   async fn read_from_apub_id(
61     object_id: Url,
62     data: &Self::DataType,
63   ) -> Result<Option<Self>, LemmyError>
64   where
65     Self: Sized,
66   {
67     let site = ApubSite::read_from_apub_id(object_id.clone(), data).await?;
68     Ok(match site {
69       Some(o) => Some(SiteOrCommunity::Site(o)),
70       None => ApubCommunity::read_from_apub_id(object_id, data)
71         .await?
72         .map(SiteOrCommunity::Community),
73     })
74   }
75
76   async fn delete(self, _data: &Self::DataType) -> Result<(), LemmyError> {
77     unimplemented!()
78   }
79
80   async fn into_apub(self, _data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
81     unimplemented!()
82   }
83
84   #[tracing::instrument(skip_all)]
85   async fn verify(
86     apub: &Self::ApubType,
87     expected_domain: &Url,
88     data: &Self::DataType,
89     request_counter: &mut i32,
90   ) -> Result<(), LemmyError> {
91     match apub {
92       InstanceOrGroup::Instance(i) => {
93         ApubSite::verify(i, expected_domain, data, request_counter).await
94       }
95       InstanceOrGroup::Group(g) => {
96         ApubCommunity::verify(g, expected_domain, data, request_counter).await
97       }
98     }
99   }
100
101   #[tracing::instrument(skip_all)]
102   async fn from_apub(
103     apub: Self::ApubType,
104     data: &Self::DataType,
105     request_counter: &mut i32,
106   ) -> Result<Self, LemmyError>
107   where
108     Self: Sized,
109   {
110     Ok(match apub {
111       InstanceOrGroup::Instance(p) => {
112         SiteOrCommunity::Site(ApubSite::from_apub(p, data, request_counter).await?)
113       }
114       InstanceOrGroup::Group(n) => {
115         SiteOrCommunity::Community(ApubCommunity::from_apub(n, data, request_counter).await?)
116       }
117     })
118   }
119 }
120
121 impl SiteOrCommunity {
122   fn id(&self) -> ObjectId<SiteOrCommunity> {
123     match self {
124       SiteOrCommunity::Site(s) => ObjectId::new(s.actor_id.clone()),
125       SiteOrCommunity::Community(c) => ObjectId::new(c.actor_id.clone()),
126     }
127   }
128 }
129
130 async fn generate_cc(target: &SiteOrCommunity, pool: &DbPool) -> Result<Vec<Url>, LemmyError> {
131   Ok(match target {
132     SiteOrCommunity::Site(_) => Site::read_remote_sites(pool)
133       .await?
134       .into_iter()
135       .map(|s| s.actor_id.into())
136       .collect(),
137     SiteOrCommunity::Community(c) => vec![c.actor_id()],
138   })
139 }
140
141 #[async_trait::async_trait(?Send)]
142 impl SendActivity for BanPerson {
143   type Response = BanPersonResponse;
144
145   async fn send_activity(
146     request: &Self,
147     _response: &Self::Response,
148     context: &LemmyContext,
149   ) -> Result<(), LemmyError> {
150     let local_user_view =
151       get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
152     let person = Person::read(context.pool(), request.person_id).await?;
153     let site = SiteOrCommunity::Site(SiteView::read_local(context.pool()).await?.site.into());
154     let expires = request.expires.map(naive_from_unix);
155
156     // if the action affects a local user, federate to other instances
157     if person.local {
158       if request.ban {
159         BlockUser::send(
160           &site,
161           &person.into(),
162           &local_user_view.person.into(),
163           request.remove_data.unwrap_or(false),
164           request.reason.clone(),
165           expires,
166           context,
167         )
168         .await
169       } else {
170         UndoBlockUser::send(
171           &site,
172           &person.into(),
173           &local_user_view.person.into(),
174           request.reason.clone(),
175           context,
176         )
177         .await
178       }
179     } else {
180       Ok(())
181     }
182   }
183 }
184
185 #[async_trait::async_trait(?Send)]
186 impl SendActivity for BanFromCommunity {
187   type Response = BanFromCommunityResponse;
188
189   async fn send_activity(
190     request: &Self,
191     _response: &Self::Response,
192     context: &LemmyContext,
193   ) -> Result<(), LemmyError> {
194     let local_user_view =
195       get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
196     let community: ApubCommunity = Community::read(context.pool(), request.community_id)
197       .await?
198       .into();
199     let banned_person: ApubPerson = Person::read(context.pool(), request.person_id)
200       .await?
201       .into();
202     let expires = request.expires.map(naive_from_unix);
203
204     if request.ban {
205       BlockUser::send(
206         &SiteOrCommunity::Community(community),
207         &banned_person,
208         &local_user_view.person.clone().into(),
209         request.remove_data.unwrap_or(false),
210         request.reason.clone(),
211         expires,
212         context,
213       )
214       .await
215     } else {
216       UndoBlockUser::send(
217         &SiteOrCommunity::Community(community),
218         &banned_person,
219         &local_user_view.person.clone().into(),
220         request.reason.clone(),
221         context,
222       )
223       .await
224     }
225   }
226 }