]> Untitled Git - lemmy.git/blob - crates/db_schema/src/impls/person.rs
Fix problem where actors can have empty public key (fixes #2347) (#2348)
[lemmy.git] / crates / db_schema / src / impls / person.rs
1 use crate::{
2   newtypes::{DbUrl, PersonId},
3   schema::person::dsl::*,
4   source::person::{Person, PersonForm, PersonSafe},
5   traits::{ApubActor, Crud},
6   utils::{functions::lower, naive_now},
7 };
8 use diesel::{
9   dsl::*,
10   result::Error,
11   ExpressionMethods,
12   PgConnection,
13   QueryDsl,
14   RunQueryDsl,
15   TextExpressionMethods,
16 };
17
18 mod safe_type {
19   use crate::{schema::person::columns::*, source::person::Person, traits::ToSafe};
20
21   type Columns = (
22     id,
23     name,
24     display_name,
25     avatar,
26     banned,
27     published,
28     updated,
29     actor_id,
30     bio,
31     local,
32     banner,
33     deleted,
34     inbox_url,
35     shared_inbox_url,
36     matrix_user_id,
37     admin,
38     bot_account,
39     ban_expires,
40   );
41
42   impl ToSafe for Person {
43     type SafeColumns = Columns;
44     fn safe_columns_tuple() -> Self::SafeColumns {
45       (
46         id,
47         name,
48         display_name,
49         avatar,
50         banned,
51         published,
52         updated,
53         actor_id,
54         bio,
55         local,
56         banner,
57         deleted,
58         inbox_url,
59         shared_inbox_url,
60         matrix_user_id,
61         admin,
62         bot_account,
63         ban_expires,
64       )
65     }
66   }
67 }
68
69 mod safe_type_alias_1 {
70   use crate::{schema::person_alias_1::columns::*, source::person::PersonAlias1, traits::ToSafe};
71
72   type Columns = (
73     id,
74     name,
75     display_name,
76     avatar,
77     banned,
78     published,
79     updated,
80     actor_id,
81     bio,
82     local,
83     banner,
84     deleted,
85     inbox_url,
86     shared_inbox_url,
87     matrix_user_id,
88     admin,
89     bot_account,
90     ban_expires,
91   );
92
93   impl ToSafe for PersonAlias1 {
94     type SafeColumns = Columns;
95     fn safe_columns_tuple() -> Self::SafeColumns {
96       (
97         id,
98         name,
99         display_name,
100         avatar,
101         banned,
102         published,
103         updated,
104         actor_id,
105         bio,
106         local,
107         banner,
108         deleted,
109         inbox_url,
110         shared_inbox_url,
111         matrix_user_id,
112         admin,
113         bot_account,
114         ban_expires,
115       )
116     }
117   }
118 }
119
120 mod safe_type_alias_2 {
121   use crate::{schema::person_alias_2::columns::*, source::person::PersonAlias2, traits::ToSafe};
122
123   type Columns = (
124     id,
125     name,
126     display_name,
127     avatar,
128     banned,
129     published,
130     updated,
131     actor_id,
132     bio,
133     local,
134     banner,
135     deleted,
136     inbox_url,
137     shared_inbox_url,
138     matrix_user_id,
139     admin,
140     bot_account,
141     ban_expires,
142   );
143
144   impl ToSafe for PersonAlias2 {
145     type SafeColumns = Columns;
146     fn safe_columns_tuple() -> Self::SafeColumns {
147       (
148         id,
149         name,
150         display_name,
151         avatar,
152         banned,
153         published,
154         updated,
155         actor_id,
156         bio,
157         local,
158         banner,
159         deleted,
160         inbox_url,
161         shared_inbox_url,
162         matrix_user_id,
163         admin,
164         bot_account,
165         ban_expires,
166       )
167     }
168   }
169 }
170
171 impl Crud for Person {
172   type Form = PersonForm;
173   type IdType = PersonId;
174   fn read(conn: &PgConnection, person_id: PersonId) -> Result<Self, Error> {
175     person
176       .filter(deleted.eq(false))
177       .find(person_id)
178       .first::<Self>(conn)
179   }
180   fn delete(conn: &PgConnection, person_id: PersonId) -> Result<usize, Error> {
181     diesel::delete(person.find(person_id)).execute(conn)
182   }
183   fn create(conn: &PgConnection, form: &PersonForm) -> Result<Self, Error> {
184     insert_into(person).values(form).get_result::<Self>(conn)
185   }
186   fn update(conn: &PgConnection, person_id: PersonId, form: &PersonForm) -> Result<Self, Error> {
187     diesel::update(person.find(person_id))
188       .set(form)
189       .get_result::<Self>(conn)
190   }
191 }
192
193 impl Person {
194   pub fn ban_person(
195     conn: &PgConnection,
196     person_id: PersonId,
197     ban: bool,
198     expires: Option<chrono::NaiveDateTime>,
199   ) -> Result<Self, Error> {
200     diesel::update(person.find(person_id))
201       .set((banned.eq(ban), ban_expires.eq(expires)))
202       .get_result::<Self>(conn)
203   }
204
205   pub fn add_admin(conn: &PgConnection, person_id: PersonId, added: bool) -> Result<Self, Error> {
206     diesel::update(person.find(person_id))
207       .set(admin.eq(added))
208       .get_result::<Self>(conn)
209   }
210
211   pub fn mark_as_updated(conn: &PgConnection, person_id: PersonId) -> Result<Person, Error> {
212     diesel::update(person.find(person_id))
213       .set((last_refreshed_at.eq(naive_now()),))
214       .get_result::<Self>(conn)
215   }
216
217   pub fn delete_account(conn: &PgConnection, person_id: PersonId) -> Result<Person, Error> {
218     use crate::schema::local_user;
219
220     // Set the local user info to none
221     diesel::update(local_user::table.filter(local_user::person_id.eq(person_id)))
222       .set((
223         local_user::email.eq::<Option<String>>(None),
224         local_user::validator_time.eq(naive_now()),
225       ))
226       .execute(conn)?;
227
228     diesel::update(person.find(person_id))
229       .set((
230         display_name.eq::<Option<String>>(None),
231         avatar.eq::<Option<String>>(None),
232         banner.eq::<Option<String>>(None),
233         bio.eq::<Option<String>>(None),
234         matrix_user_id.eq::<Option<String>>(None),
235         deleted.eq(true),
236         updated.eq(naive_now()),
237       ))
238       .get_result::<Self>(conn)
239   }
240
241   pub fn upsert(conn: &PgConnection, person_form: &PersonForm) -> Result<Person, Error> {
242     insert_into(person)
243       .values(person_form)
244       .on_conflict(actor_id)
245       .do_update()
246       .set(person_form)
247       .get_result::<Self>(conn)
248   }
249
250   pub fn update_deleted(
251     conn: &PgConnection,
252     person_id: PersonId,
253     new_deleted: bool,
254   ) -> Result<Person, Error> {
255     use crate::schema::person::dsl::*;
256     diesel::update(person.find(person_id))
257       .set(deleted.eq(new_deleted))
258       .get_result::<Self>(conn)
259   }
260
261   pub fn is_banned(&self) -> bool {
262     is_banned(self.banned, self.ban_expires)
263   }
264
265   pub fn leave_admin(conn: &PgConnection, person_id: PersonId) -> Result<Self, Error> {
266     diesel::update(person.find(person_id))
267       .set(admin.eq(false))
268       .get_result::<Self>(conn)
269   }
270
271   pub fn remove_avatar_and_banner(conn: &PgConnection, person_id: PersonId) -> Result<Self, Error> {
272     diesel::update(person.find(person_id))
273       .set((
274         avatar.eq::<Option<String>>(None),
275         banner.eq::<Option<String>>(None),
276       ))
277       .get_result::<Self>(conn)
278   }
279 }
280
281 impl PersonSafe {
282   pub fn is_banned(&self) -> bool {
283     is_banned(self.banned, self.ban_expires)
284   }
285 }
286
287 fn is_banned(banned_: bool, expires: Option<chrono::NaiveDateTime>) -> bool {
288   if let Some(expires) = expires {
289     banned_ && expires.gt(&naive_now())
290   } else {
291     banned_
292   }
293 }
294
295 impl ApubActor for Person {
296   fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Option<Self>, Error> {
297     use crate::schema::person::dsl::*;
298     Ok(
299       person
300         .filter(deleted.eq(false))
301         .filter(actor_id.eq(object_id))
302         .first::<Person>(conn)
303         .ok()
304         .map(Into::into),
305     )
306   }
307
308   fn read_from_name(
309     conn: &PgConnection,
310     from_name: &str,
311     include_deleted: bool,
312   ) -> Result<Person, Error> {
313     let mut q = person
314       .into_boxed()
315       .filter(local.eq(true))
316       .filter(lower(name).eq(lower(from_name)));
317     if !include_deleted {
318       q = q.filter(deleted.eq(false))
319     }
320     q.first::<Self>(conn)
321   }
322
323   fn read_from_name_and_domain(
324     conn: &PgConnection,
325     person_name: &str,
326     protocol_domain: &str,
327   ) -> Result<Person, Error> {
328     use crate::schema::person::dsl::*;
329     person
330       .filter(lower(name).eq(lower(person_name)))
331       .filter(actor_id.like(format!("{}%", protocol_domain)))
332       .first::<Self>(conn)
333   }
334 }
335
336 #[cfg(test)]
337 mod tests {
338   use crate::{source::person::*, traits::Crud, utils::establish_unpooled_connection};
339
340   #[test]
341   fn test_crud() {
342     let conn = establish_unpooled_connection();
343
344     let new_person = PersonForm {
345       name: "holly".into(),
346       public_key: Some("nada".to_owned()),
347       ..PersonForm::default()
348     };
349
350     let inserted_person = Person::create(&conn, &new_person).unwrap();
351
352     let expected_person = Person {
353       id: inserted_person.id,
354       name: "holly".into(),
355       display_name: None,
356       avatar: None,
357       banner: None,
358       banned: false,
359       deleted: false,
360       published: inserted_person.published,
361       updated: None,
362       actor_id: inserted_person.actor_id.to_owned(),
363       bio: None,
364       local: true,
365       bot_account: false,
366       admin: false,
367       private_key: None,
368       public_key: "nada".to_owned(),
369       last_refreshed_at: inserted_person.published,
370       inbox_url: inserted_person.inbox_url.to_owned(),
371       shared_inbox_url: None,
372       matrix_user_id: None,
373       ban_expires: None,
374     };
375
376     let read_person = Person::read(&conn, inserted_person.id).unwrap();
377     let updated_person = Person::update(&conn, inserted_person.id, &new_person).unwrap();
378     let num_deleted = Person::delete(&conn, inserted_person.id).unwrap();
379
380     assert_eq!(expected_person, read_person);
381     assert_eq!(expected_person, inserted_person);
382     assert_eq!(expected_person, updated_person);
383     assert_eq!(1, num_deleted);
384   }
385 }