]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/mod.rs
Pleroma federation2 (#1855)
[lemmy.git] / crates / apub / src / activities / mod.rs
1 use crate::{
2   check_community_or_site_ban,
3   check_is_apub_id_valid,
4   fetcher::object_id::ObjectId,
5   generate_moderators_url,
6   objects::{community::ApubCommunity, person::ApubPerson},
7 };
8 use activitystreams::public;
9 use anyhow::anyhow;
10 use lemmy_api_common::blocking;
11 use lemmy_apub_lib::{traits::ActivityFields, verify::verify_domains_match};
12 use lemmy_db_schema::source::community::Community;
13 use lemmy_db_views_actor::community_view::CommunityView;
14 use lemmy_utils::{settings::structs::Settings, LemmyError};
15 use lemmy_websocket::LemmyContext;
16 use serde::{Deserialize, Serialize};
17 use std::ops::Deref;
18 use strum_macros::ToString;
19 use url::{ParseError, Url};
20 use uuid::Uuid;
21
22 pub mod comment;
23 pub mod community;
24 pub mod deletion;
25 pub mod following;
26 pub mod post;
27 pub mod private_message;
28 pub mod report;
29 pub mod undo_remove;
30 pub mod voting;
31
32 #[derive(Clone, Debug, ToString, Deserialize, Serialize)]
33 pub enum CreateOrUpdateType {
34   Create,
35   Update,
36 }
37
38 /// Checks that the specified Url actually identifies a Person (by fetching it), and that the person
39 /// doesn't have a site ban.
40 async fn verify_person(
41   person_id: &ObjectId<ApubPerson>,
42   context: &LemmyContext,
43   request_counter: &mut i32,
44 ) -> Result<(), LemmyError> {
45   let person = person_id.dereference(context, request_counter).await?;
46   if person.banned {
47     return Err(anyhow!("Person {} is banned", person_id).into());
48   }
49   Ok(())
50 }
51
52 pub(crate) async fn extract_community(
53   cc: &[Url],
54   context: &LemmyContext,
55   request_counter: &mut i32,
56 ) -> Result<ApubCommunity, LemmyError> {
57   let mut cc_iter = cc.iter();
58   loop {
59     if let Some(cid) = cc_iter.next() {
60       let cid = ObjectId::new(cid.clone());
61       if let Ok(c) = cid.dereference(context, request_counter).await {
62         break Ok(c);
63       }
64     } else {
65       return Err(anyhow!("No community found in cc").into());
66     }
67   }
68 }
69
70 /// Fetches the person and community to verify their type, then checks if person is banned from site
71 /// or community.
72 pub(crate) async fn verify_person_in_community(
73   person_id: &ObjectId<ApubPerson>,
74   community_id: &ObjectId<ApubCommunity>,
75   context: &LemmyContext,
76   request_counter: &mut i32,
77 ) -> Result<(), LemmyError> {
78   let community = community_id.dereference(context, request_counter).await?;
79   let person = person_id.dereference(context, request_counter).await?;
80   check_community_or_site_ban(person.deref(), community.id, context.pool()).await
81 }
82
83 /// Simply check that the url actually refers to a valid group.
84 async fn verify_community(
85   community_id: &ObjectId<ApubCommunity>,
86   context: &LemmyContext,
87   request_counter: &mut i32,
88 ) -> Result<(), LemmyError> {
89   community_id.dereference(context, request_counter).await?;
90   Ok(())
91 }
92
93 fn verify_activity(activity: &dyn ActivityFields, settings: &Settings) -> Result<(), LemmyError> {
94   check_is_apub_id_valid(activity.actor(), false, settings)?;
95   verify_domains_match(activity.id_unchecked(), activity.actor())?;
96   Ok(())
97 }
98
99 /// Verify that the actor is a community mod. This check is only run if the community is local,
100 /// because in case of remote communities, admins can also perform mod actions. As admin status
101 /// is not federated, we cant verify their actions remotely.
102 pub(crate) async fn verify_mod_action(
103   actor_id: &ObjectId<ApubPerson>,
104   community_id: &ObjectId<ApubCommunity>,
105   context: &LemmyContext,
106   request_counter: &mut i32,
107 ) -> Result<(), LemmyError> {
108   let community = community_id.dereference_local(context).await?;
109
110   if community.local {
111     let actor = actor_id.dereference(context, request_counter).await?;
112
113     // Note: this will also return true for admins in addition to mods, but as we dont know about
114     //       remote admins, it doesnt make any difference.
115     let community_id = community.id;
116     let actor_id = actor.id;
117     let is_mod_or_admin = blocking(context.pool(), move |conn| {
118       CommunityView::is_mod_or_admin(conn, actor_id, community_id)
119     })
120     .await?;
121     if !is_mod_or_admin {
122       return Err(anyhow!("Not a mod").into());
123     }
124   }
125   Ok(())
126 }
127
128 /// For Add/Remove community moderator activities, check that the target field actually contains
129 /// /c/community/moderators. Any different values are unsupported.
130 fn verify_add_remove_moderator_target(
131   target: &Url,
132   community: &ObjectId<ApubCommunity>,
133 ) -> Result<(), LemmyError> {
134   if target != &generate_moderators_url(&community.clone().into())?.into_inner() {
135     return Err(anyhow!("Unkown target url").into());
136   }
137   Ok(())
138 }
139
140 pub(crate) fn verify_is_public(to: &[Url]) -> Result<(), LemmyError> {
141   if !to.contains(&public()) {
142     return Err(anyhow!("Object is not public").into());
143   }
144   Ok(())
145 }
146
147 pub(crate) fn check_community_deleted_or_removed(community: &Community) -> Result<(), LemmyError> {
148   if community.deleted || community.removed {
149     Err(anyhow!("New post or comment cannot be created in deleted or removed community").into())
150   } else {
151     Ok(())
152   }
153 }
154
155 /// Generate a unique ID for an activity, in the format:
156 /// `http(s)://example.com/receive/create/202daf0a-1489-45df-8d2e-c8a3173fed36`
157 fn generate_activity_id<T>(kind: T, protocol_and_hostname: &str) -> Result<Url, ParseError>
158 where
159   T: ToString,
160 {
161   let id = format!(
162     "{}/activities/{}/{}",
163     protocol_and_hostname,
164     kind.to_string().to_lowercase(),
165     Uuid::new_v4()
166   );
167   Url::parse(&id)
168 }