]> Untitled Git - lemmy.git/blob - server/src/apub/user.rs
Make reads from activitypub objects immutable
[lemmy.git] / server / src / apub / user.rs
1 use crate::{
2   api::claims::Claims,
3   apub::{
4     activities::send_activity, create_apub_response, insert_activity, ActorType, FromApub,
5     PersonExt, ToApub,
6   },
7   blocking,
8   routes::DbPoolParam,
9   DbPool, LemmyError,
10 };
11 use activitystreams_ext::Ext1;
12 use activitystreams_new::{
13   activity::{Follow, Undo},
14   actor::{ApActor, Endpoints, Person},
15   context,
16   object::{Image, Tombstone},
17   prelude::*,
18   primitives::{XsdAnyUri, XsdDateTime},
19 };
20 use actix_web::{body::Body, client::Client, web, HttpResponse};
21 use failure::_core::str::FromStr;
22 use lemmy_db::{
23   naive_now,
24   user::{UserForm, User_},
25 };
26 use lemmy_utils::convert_datetime;
27 use serde::Deserialize;
28
29 #[derive(Deserialize)]
30 pub struct UserQuery {
31   user_name: String,
32 }
33
34 #[async_trait::async_trait(?Send)]
35 impl ToApub for User_ {
36   type Response = PersonExt;
37
38   // Turn a Lemmy Community into an ActivityPub group that can be sent out over the network.
39   async fn to_apub(&self, _pool: &DbPool) -> Result<PersonExt, LemmyError> {
40     // TODO go through all these to_string and to_owned()
41     let mut person = Person::new();
42     person
43       .set_context(context())
44       .set_id(XsdAnyUri::from_str(&self.actor_id)?)
45       .set_name(self.name.to_owned())
46       .set_published(XsdDateTime::from(convert_datetime(self.published)));
47
48     if let Some(u) = self.updated {
49       person.set_updated(XsdDateTime::from(convert_datetime(u)));
50     }
51
52     if let Some(avatar_url) = &self.avatar {
53       let mut image = Image::new();
54       image.set_url(avatar_url.to_owned());
55       person.set_icon(image.into_any_base()?);
56     }
57
58     let mut ap_actor = ApActor::new(self.get_inbox_url().parse()?, person);
59     ap_actor
60       .set_outbox(self.get_outbox_url().parse()?)
61       .set_followers(self.get_followers_url().parse()?)
62       .set_following(self.get_following_url().parse()?)
63       .set_liked(self.get_liked_url().parse()?)
64       .set_endpoints(Endpoints {
65         shared_inbox: Some(self.get_shared_inbox_url().parse()?),
66         ..Default::default()
67       });
68
69     if let Some(i) = &self.preferred_username {
70       ap_actor.set_preferred_username(i.to_owned());
71     }
72
73     Ok(Ext1::new(ap_actor, self.get_public_key_ext()))
74   }
75   fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
76     unimplemented!()
77   }
78 }
79
80 #[async_trait::async_trait(?Send)]
81 impl ActorType for User_ {
82   fn actor_id(&self) -> String {
83     self.actor_id.to_owned()
84   }
85
86   fn public_key(&self) -> String {
87     self.public_key.to_owned().unwrap()
88   }
89
90   fn private_key(&self) -> String {
91     self.private_key.to_owned().unwrap()
92   }
93
94   /// As a given local user, send out a follow request to a remote community.
95   async fn send_follow(
96     &self,
97     follow_actor_id: &str,
98     client: &Client,
99     pool: &DbPool,
100   ) -> Result<(), LemmyError> {
101     let id = format!("{}/follow/{}", self.actor_id, uuid::Uuid::new_v4());
102     let mut follow = Follow::new(self.actor_id.to_owned(), follow_actor_id);
103     follow.set_context(context()).set_id(id.parse()?);
104     let to = format!("{}/inbox", follow_actor_id);
105
106     insert_activity(self.id, follow.clone(), true, pool).await?;
107
108     send_activity(client, &follow, self, vec![to]).await?;
109     Ok(())
110   }
111
112   async fn send_unfollow(
113     &self,
114     follow_actor_id: &str,
115     client: &Client,
116     pool: &DbPool,
117   ) -> Result<(), LemmyError> {
118     let id = format!("{}/follow/{}", self.actor_id, uuid::Uuid::new_v4());
119     let mut follow = Follow::new(self.actor_id.to_owned(), follow_actor_id);
120     follow.set_context(context()).set_id(id.parse()?);
121
122     let to = format!("{}/inbox", follow_actor_id);
123
124     // TODO
125     // Undo that fake activity
126     let undo_id = format!("{}/undo/follow/{}", self.actor_id, uuid::Uuid::new_v4());
127     let mut undo = Undo::new(self.actor_id.parse::<XsdAnyUri>()?, follow.into_any_base()?);
128     undo.set_context(context()).set_id(undo_id.parse()?);
129
130     insert_activity(self.id, undo.clone(), true, pool).await?;
131
132     send_activity(client, &undo, self, vec![to]).await?;
133     Ok(())
134   }
135
136   async fn send_delete(
137     &self,
138     _creator: &User_,
139     _client: &Client,
140     _pool: &DbPool,
141   ) -> Result<(), LemmyError> {
142     unimplemented!()
143   }
144
145   async fn send_undo_delete(
146     &self,
147     _creator: &User_,
148     _client: &Client,
149     _pool: &DbPool,
150   ) -> Result<(), LemmyError> {
151     unimplemented!()
152   }
153
154   async fn send_remove(
155     &self,
156     _creator: &User_,
157     _client: &Client,
158     _pool: &DbPool,
159   ) -> Result<(), LemmyError> {
160     unimplemented!()
161   }
162
163   async fn send_undo_remove(
164     &self,
165     _creator: &User_,
166     _client: &Client,
167     _pool: &DbPool,
168   ) -> Result<(), LemmyError> {
169     unimplemented!()
170   }
171
172   async fn send_accept_follow(
173     &self,
174     _follow: &Follow,
175     _client: &Client,
176     _pool: &DbPool,
177   ) -> Result<(), LemmyError> {
178     unimplemented!()
179   }
180
181   async fn get_follower_inboxes(&self, _pool: &DbPool) -> Result<Vec<String>, LemmyError> {
182     unimplemented!()
183   }
184 }
185
186 #[async_trait::async_trait(?Send)]
187 impl FromApub for UserForm {
188   type ApubType = PersonExt;
189   /// Parse an ActivityPub person received from another instance into a Lemmy user.
190   async fn from_apub(person: &PersonExt, _: &Client, _: &DbPool) -> Result<Self, LemmyError> {
191     let avatar = match person.icon() {
192       Some(any_image) => Image::from_any_base(any_image.as_one().unwrap().clone())
193         .unwrap()
194         .unwrap()
195         .url
196         .unwrap()
197         .as_single_xsd_any_uri()
198         .map(|u| u.to_string()),
199       None => None,
200     };
201
202     Ok(UserForm {
203       name: person
204         .name()
205         .unwrap()
206         .as_single_xsd_string()
207         .unwrap()
208         .into(),
209       preferred_username: person.inner.preferred_username().map(|u| u.to_string()),
210       password_encrypted: "".to_string(),
211       admin: false,
212       banned: false,
213       email: None,
214       avatar,
215       updated: person
216         .updated()
217         .map(|u| u.as_ref().to_owned().naive_local()),
218       show_nsfw: false,
219       theme: "".to_string(),
220       default_sort_type: 0,
221       default_listing_type: 0,
222       lang: "".to_string(),
223       show_avatars: false,
224       send_notifications_to_email: false,
225       matrix_user_id: None,
226       actor_id: person.id().unwrap().to_string(),
227       bio: person
228         .summary()
229         .map(|s| s.as_single_xsd_string().unwrap().into()),
230       local: false,
231       private_key: None,
232       public_key: Some(person.ext_one.public_key.to_owned().public_key_pem),
233       last_refreshed_at: Some(naive_now()),
234     })
235   }
236 }
237
238 /// Return the user json over HTTP.
239 pub async fn get_apub_user_http(
240   info: web::Path<UserQuery>,
241   db: DbPoolParam,
242 ) -> Result<HttpResponse<Body>, LemmyError> {
243   let user_name = info.into_inner().user_name;
244   let user = blocking(&db, move |conn| {
245     Claims::find_by_email_or_username(conn, &user_name)
246   })
247   .await??;
248   let u = user.to_apub(&db).await?;
249   Ok(create_apub_response(&u))
250 }