1use super::common::NAME_REGEX;
2use oiseau::cache::Cache;
3use crate::model::{
4 Error, Result,
5 auth::{Token, User, UserSettings},
6 permissions::{FinePermission, SecondaryPermission},
7 oauth::AuthGrant,
8 moderation::AuditLogEntry,
9 auth::{
10 Achievement, AchievementName, AchievementRarity, Notification, UserConnections,
11 ACHIEVEMENTS,
12 },
13};
14use tetratto_shared::{
15 hash::{hash_salted, salt},
16 unix_epoch_timestamp,
17};
18use crate::{auto_method, DataManager};
19use oiseau::{PostgresRow, execute, get, query_row, params};
20
21macro_rules! update_role_fn {
22 ($name:ident, $role_ty:ty, $col:literal) => {
23 pub async fn $name(
24 &self,
25 id: usize,
26 role: $role_ty,
27 user: &User,
28 force: bool,
29 ) -> Result<()> {
30 let other_user = self.get_user_by_id(id).await?;
31
32 if !force {
33 if !user.permissions.check(FinePermission::MANAGE_USERS) {
35 return Err(Error::NotAllowed);
36 }
37
38 if other_user.permissions.check_manager() && !user.permissions.check_admin() {
39 return Err(Error::MiscError(
40 "Cannot manage the role of other managers".to_string(),
41 ));
42 }
43
44 if other_user.permissions == user.permissions {
45 return Err(Error::MiscError(
46 "Cannot manage users of equal level to you".to_string(),
47 ));
48 }
49 }
50
51 let conn = match self.0.connect().await {
53 Ok(c) => c,
54 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
55 };
56
57 let res = execute!(
58 &conn,
59 &format!("UPDATE users SET {} = $1 WHERE id = $2", $col),
60 params![&(role.bits() as i32), &(id as i64)]
61 );
62
63 if let Err(e) = res {
64 return Err(Error::DatabaseError(e.to_string()));
65 }
66
67 self.cache_clear_user(&other_user).await;
68
69 self.create_audit_log_entry(AuditLogEntry::new(
71 user.id,
72 format!(
73 "invoked `{}` with x value `{}` and y value `{}`",
74 $col,
75 other_user.id,
76 role.bits()
77 ),
78 ))
79 .await?;
80
81 Ok(())
83 }
84 };
85}
86
87impl DataManager {
88 pub(crate) fn get_user_from_row(x: &PostgresRow) -> User {
90 User {
91 id: get!(x->0(i64)) as usize,
92 created: get!(x->1(i64)) as usize,
93 username: get!(x->2(String)),
94 password: get!(x->3(String)),
95 salt: get!(x->4(String)),
96 settings: serde_json::from_str(&get!(x->5(String)).to_string()).unwrap(),
97 tokens: serde_json::from_str(&get!(x->6(String)).to_string()).unwrap(),
98 permissions: FinePermission::from_bits(get!(x->7(i32)) as u32).unwrap(),
99 is_verified: get!(x->8(i32)) as i8 == 1,
100 notification_count: {
101 let x = get!(x->9(i32)) as usize;
102 if x > usize::MAX - 1000 { 0 } else { x }
104 },
105 follower_count: get!(x->10(i32)) as usize,
106 following_count: get!(x->11(i32)) as usize,
107 last_seen: get!(x->12(i64)) as usize,
108 totp: get!(x->13(String)),
109 recovery_codes: serde_json::from_str(&get!(x->14(String)).to_string()).unwrap(),
110 post_count: get!(x->15(i32)) as usize,
111 request_count: {
112 let x = get!(x->16(i32)) as usize;
113 if x > usize::MAX - 1000 { 0 } else { x }
114 },
115 connections: serde_json::from_str(&get!(x->17(String)).to_string()).unwrap(),
116 stripe_id: get!(x->18(String)),
117 grants: serde_json::from_str(&get!(x->19(String)).to_string()).unwrap(),
118 associated: serde_json::from_str(&get!(x->20(String)).to_string()).unwrap(),
119 invite_code: get!(x->21(i64)) as usize,
120 secondary_permissions: SecondaryPermission::from_bits(get!(x->22(i32)) as u32).unwrap(),
121 achievements: serde_json::from_str(&get!(x->23(String)).to_string()).unwrap(),
122 awaiting_purchase: get!(x->24(i32)) as i8 == 1,
123 was_purchased: get!(x->25(i32)) as i8 == 1,
124 ban_reason: get!(x->26(String)),
125 is_deactivated: get!(x->27(i32)) as i8 == 1,
126 ban_expire: get!(x->28(i64)) as usize,
127 checkouts: serde_json::from_str(&get!(x->29(String)).to_string()).unwrap(),
128 last_policy_consent: get!(x->30(i64)) as usize,
129 close_friends_stack: get!(x->31(i64)) as usize,
130 missed_messages_count: get!(x->32(i32)) as usize,
131 views: get!(x->33(i32)) as usize,
132 shrimpcamp_link: get!(x->34(i64)) as usize,
133 }
134 }
135
136 auto_method!(get_user_by_id(usize as i64)@get_user_from_row -> "SELECT * FROM users WHERE id = $1" --name="user" --returns=User --cache-key-tmpl="atto.user:{}");
137 auto_method!(get_user_by_username(&str)@get_user_from_row -> "SELECT * FROM users WHERE username = $1" --name="user" --returns=User --cache-key-tmpl="atto.user:{}");
138 auto_method!(get_user_by_username_no_cache(&str)@get_user_from_row -> "SELECT * FROM users WHERE username = $1" --name="user" --returns=User);
139 auto_method!(get_user_by_browser_session(&str)@get_user_from_row -> "SELECT * FROM users WHERE browser_session = $1" --name="user" --returns=User);
140
141 pub async fn get_user_by_id_with_void(&self, id: usize) -> Result<User> {
146 let conn = match self.0.connect().await {
147 Ok(c) => c,
148 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
149 };
150
151 let res = query_row!(
152 &conn,
153 "SELECT * FROM users WHERE id = $1",
154 &[&(id as i64)],
155 |x| Ok(Self::get_user_from_row(x))
156 );
157
158 if res.is_err() {
159 return Ok(User::deleted());
160 }
162
163 Ok(res.unwrap())
164 }
165
166 pub async fn get_user_by_token(&self, token: &str) -> Result<User> {
171 let conn = match self.0.connect().await {
172 Ok(c) => c,
173 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
174 };
175
176 let res = query_row!(
177 &conn,
178 "SELECT * FROM users WHERE tokens LIKE $1",
179 &[&format!("%\"{token}\"%")],
180 |x| Ok(Self::get_user_from_row(x))
181 );
182
183 if res.is_err() {
184 return Err(Error::UserNotFound);
185 }
186
187 Ok(res.unwrap())
188 }
189
190 pub async fn get_user_by_grant_token(
197 &self,
198 token: &str,
199 check_expiration: bool,
200 ) -> Result<(AuthGrant, User)> {
201 let conn = match self.0.connect().await {
202 Ok(c) => c,
203 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
204 };
205
206 let res = query_row!(
207 &conn,
208 "SELECT * FROM users WHERE grants LIKE $1",
209 &[&format!("%\"token\":\"{token}\"%")],
210 |x| Ok(Self::get_user_from_row(x))
211 );
212
213 if res.is_err() {
214 return Err(Error::UserNotFound);
215 }
216
217 let user = res.unwrap();
218 let grant = user
219 .grants
220 .iter()
221 .find(|x| x.token == token)
222 .unwrap()
223 .clone();
224
225 if check_expiration {
227 let now = unix_epoch_timestamp();
228 let delta = now - grant.last_updated;
229
230 if delta > 604_800_000 {
231 return Err(Error::MiscError("Token expired".to_string()));
232 }
233 }
234
235 Ok((grant, user))
237 }
238
239 pub async fn create_user(&self, mut data: User) -> Result<()> {
244 if !self.0.0.security.registration_enabled {
245 return Err(Error::RegistrationDisabled);
246 }
247
248 data.username = data.username.to_lowercase();
249
250 if data.username.len() < 2 {
252 return Err(Error::DataTooShort("username".to_string()));
253 } else if data.username.len() > 32 {
254 return Err(Error::DataTooLong("username".to_string()));
255 }
256
257 if data.password.len() < 6 {
258 return Err(Error::DataTooShort("password".to_string()));
259 }
260
261 if self.0.0.banned_usernames.contains(&data.username) {
262 return Err(Error::MiscError("This username cannot be used".to_string()));
263 }
264
265 let regex = regex::RegexBuilder::new(NAME_REGEX)
266 .multi_line(true)
267 .build()
268 .unwrap();
269
270 if regex.captures(&data.username).is_some() {
271 return Err(Error::MiscError(
272 "This username contains invalid characters".to_string(),
273 ));
274 }
275
276 if self.get_user_by_username(&data.username).await.is_ok() {
278 return Err(Error::UsernameInUse);
279 }
280
281 let conn = match self.0.connect().await {
283 Ok(c) => c,
284 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
285 };
286
287 let res = execute!(
288 &conn,
289 "INSERT INTO users VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33, $34, $35)",
290 params![
291 &(data.id as i64),
292 &(data.created as i64),
293 &data.username.to_lowercase(),
294 &data.password,
295 &data.salt,
296 &serde_json::to_string(&data.settings).unwrap(),
297 &serde_json::to_string(&data.tokens).unwrap(),
298 &(FinePermission::DEFAULT.bits() as i32),
299 &if data.is_verified { 1_i32 } else { 0_i32 },
300 &0_i32,
301 &0_i32,
302 &0_i32,
303 &(data.last_seen as i64),
304 &String::new(),
305 "[]",
306 &0_i32,
307 &0_i32,
308 &serde_json::to_string(&data.connections).unwrap(),
309 &"",
310 &serde_json::to_string(&data.grants).unwrap(),
311 &serde_json::to_string(&data.associated).unwrap(),
312 &(data.invite_code as i64),
313 &(SecondaryPermission::DEFAULT.bits() as i32),
314 &serde_json::to_string(&data.achievements).unwrap(),
315 &if data.awaiting_purchase { 1_i32 } else { 0_i32 },
316 &if data.was_purchased { 1_i32 } else { 0_i32 },
317 &data.ban_reason,
318 &if data.is_deactivated { 1_i32 } else { 0_i32 },
319 &(data.ban_expire as i64),
320 &serde_json::to_string(&data.checkouts).unwrap(),
321 &(data.last_policy_consent as i64),
322 &(data.close_friends_stack as i64),
323 &(data.missed_messages_count as i32),
324 &(data.views as i32),
325 &(data.shrimpcamp_link as i64),
326 ]
327 );
328
329 if let Err(e) = res {
330 return Err(Error::DatabaseError(e.to_string()));
331 }
332
333 Ok(())
334 }
335
336 pub async fn delete_user(&self, id: usize, password: &str, force: bool) -> Result<User> {
343 let user = self.get_user_by_id(id).await?;
344
345 if (hash_salted(password.to_string(), user.salt.clone()) != user.password) && !force {
346 return Err(Error::IncorrectPassword);
347 }
348
349 let conn = match self.0.connect().await {
350 Ok(c) => c,
351 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
352 };
353
354 let res = execute!(&conn, "DELETE FROM users WHERE id = $1", &[&(id as i64)]);
355
356 if let Err(e) = res {
357 return Err(Error::DatabaseError(e.to_string()));
358 }
359
360 self.cache_clear_user(&user).await;
361
362 for community in self.get_communities_by_owner(user.id).await? {
364 self.delete_community(community.id, &user).await?;
365 }
366
367 let res = execute!(
370 &conn,
371 "DELETE FROM memberships WHERE owner = $1",
372 &[&(id as i64)]
373 );
374
375 if let Err(e) = res {
376 return Err(Error::DatabaseError(e.to_string()));
377 }
378
379 let res = execute!(
381 &conn,
382 "DELETE FROM notifications WHERE owner = $1",
383 &[&(id as i64)]
384 );
385
386 if let Err(e) = res {
387 return Err(Error::DatabaseError(e.to_string()));
388 }
389
390 let res = execute!(
392 &conn,
393 "DELETE FROM requests WHERE owner = $1",
394 &[&(id as i64)]
395 );
396
397 if let Err(e) = res {
398 return Err(Error::DatabaseError(e.to_string()));
399 }
400
401 let res = execute!(
403 &conn,
404 "DELETE FROM user_warnings WHERE receiver = $1",
405 &[&(id as i64)]
406 );
407
408 if let Err(e) = res {
409 return Err(Error::DatabaseError(e.to_string()));
410 }
411
412 let res = execute!(
414 &conn,
415 "DELETE FROM userblocks WHERE initiator = $1 OR receiver = $1",
416 &[&(id as i64)]
417 );
418
419 if let Err(e) = res {
420 return Err(Error::DatabaseError(e.to_string()));
421 }
422
423 let res = execute!(
424 &conn,
425 "DELETE FROM ipblocks WHERE initiator = $1",
426 &[&(id as i64)]
427 );
428
429 if let Err(e) = res {
430 return Err(Error::DatabaseError(e.to_string()));
431 }
432
433 let res = execute!(
436 &conn,
437 "DELETE FROM reactions WHERE owner = $1",
438 &[&(id as i64)]
439 );
440
441 if let Err(e) = res {
442 return Err(Error::DatabaseError(e.to_string()));
443 }
444
445 let res = execute!(
447 &conn,
448 "DELETE FROM stacks WHERE owner = $1",
449 &[&(id as i64)]
450 );
451
452 if let Err(e) = res {
453 return Err(Error::DatabaseError(e.to_string()));
454 }
455
456 let res = execute!(
458 &conn,
459 "DELETE FROM drafts WHERE owner = $1",
460 &[&(id as i64)]
461 );
462
463 if let Err(e) = res {
464 return Err(Error::DatabaseError(e.to_string()));
465 }
466
467 let res = execute!(&conn, "DELETE FROM posts WHERE owner = $1", &[&(id as i64)]);
469
470 if let Err(e) = res {
471 return Err(Error::DatabaseError(e.to_string()));
472 }
473
474 let res = execute!(&conn, "DELETE FROM polls WHERE owner = $1", &[&(id as i64)]);
476
477 if let Err(e) = res {
478 return Err(Error::DatabaseError(e.to_string()));
479 }
480
481 let res = execute!(
483 &conn,
484 "DELETE FROM pollvotes WHERE owner = $1",
485 &[&(id as i64)]
486 );
487
488 if let Err(e) = res {
489 return Err(Error::DatabaseError(e.to_string()));
490 }
491
492 let res = execute!(
494 &conn,
495 "DELETE FROM stackblocks WHERE initiator = $1",
496 &[&(id as i64)]
497 );
498
499 if let Err(e) = res {
500 return Err(Error::DatabaseError(e.to_string()));
501 }
502
503 let res = execute!(
505 &conn,
506 "DELETE FROM invite_codes WHERE owner = $1",
507 &[&(id as i64)]
508 );
509
510 if let Err(e) = res {
511 return Err(Error::DatabaseError(e.to_string()));
512 }
513
514 let res = execute!(
516 &conn,
517 "DELETE FROM guest_logs WHERE owner = $1",
518 &[&(id as i64)]
519 );
520
521 if let Err(e) = res {
522 return Err(Error::DatabaseError(e.to_string()));
523 }
524
525 let res = execute!(
527 &conn,
528 "DELETE FROM transfers WHERE sender = $1 OR receiver = $1",
529 &[&(id as i64)]
530 );
531
532 if let Err(e) = res {
533 return Err(Error::DatabaseError(e.to_string()));
534 }
535
536 let res = execute!(
538 &conn,
539 "DELETE FROM products WHERE owner = $1",
540 &[&(id as i64)]
541 );
542
543 if let Err(e) = res {
544 return Err(Error::DatabaseError(e.to_string()));
545 }
546
547 let res = execute!(
549 &conn,
550 "DELETE FROM letters WHERE owner = $1",
551 &[&(id as i64)]
552 );
553
554 if let Err(e) = res {
555 return Err(Error::DatabaseError(e.to_string()));
556 }
557
558 let res = execute!(&conn, "DELETE FROM ads WHERE owner = $1", &[&(id as i64)]);
560
561 if let Err(e) = res {
562 return Err(Error::DatabaseError(e.to_string()));
563 }
564
565 for follow in self.get_userfollows_by_receiver_all(id).await? {
567 self.delete_userfollow(follow.id, &user, true).await?;
568 }
569
570 for follow in self.get_userfollows_by_initiator_all(id).await? {
571 self.delete_userfollow(follow.id, &user, true).await?;
572 }
573
574 for app in self.get_apps_by_owner(id).await? {
576 self.delete_app(app.id, &user).await?;
577 }
578
579 for upload in match self.2.get_uploads_by_owner_all(user.id).await {
581 Ok(x) => x,
582 Err(e) => return Err(Error::MiscError(e.to_string())),
583 } {
584 if let Err(e) = self.2.delete_upload(upload.id).await {
585 return Err(Error::MiscError(e.to_string()));
586 }
587 }
588
589 for poll in self.get_polls_by_owner_all(user.id).await? {
591 self.delete_poll(poll.id, &user).await?;
592 }
593
594 if self.0.0.security.enable_invite_codes
596 && user.invite_code != 0
597 && self.get_invite_code_by_id(user.invite_code).await.is_ok()
598 {
599 self.update_invite_code_is_used(user.invite_code, false)
602 .await?;
603 }
604
605 Ok(user)
607 }
608
609 pub async fn update_user_verified_status(&self, id: usize, x: bool, user: User) -> Result<()> {
610 if !user.permissions.check(FinePermission::MANAGE_VERIFIED) {
611 return Err(Error::NotAllowed);
612 }
613
614 let other_user = self.get_user_by_id(id).await?;
615
616 let conn = match self.0.connect().await {
617 Ok(c) => c,
618 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
619 };
620
621 let res = execute!(
622 &conn,
623 "UPDATE users SET verified = $1 WHERE id = $2",
624 params![&{ if x { 1 } else { 0 } }, &(id as i64)]
625 );
626
627 if let Err(e) = res {
628 return Err(Error::DatabaseError(e.to_string()));
629 }
630
631 self.cache_clear_user(&other_user).await;
632
633 self.create_audit_log_entry(AuditLogEntry::new(
635 user.id,
636 format!(
637 "invoked `update_user_verified_status` with x value `{}` and y value `{}`",
638 other_user.id, x
639 ),
640 ))
641 .await?;
642
643 Ok(())
645 }
646
647 pub async fn update_user_is_deactivated(&self, id: usize, x: bool, user: User) -> Result<()> {
648 if id != user.id && !user.permissions.check(FinePermission::MANAGE_USERS) {
649 return Err(Error::NotAllowed);
650 }
651
652 let other_user = self.get_user_by_id(id).await?;
653
654 let conn = match self.0.connect().await {
655 Ok(c) => c,
656 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
657 };
658
659 let res = execute!(
660 &conn,
661 "UPDATE users SET is_deactivated = $1 WHERE id = $2",
662 params![&{ if x { 1 } else { 0 } }, &(id as i64)]
663 );
664
665 if let Err(e) = res {
666 return Err(Error::DatabaseError(e.to_string()));
667 }
668
669 self.cache_clear_user(&other_user).await;
670
671 if user.id != other_user.id {
673 self.create_audit_log_entry(AuditLogEntry::new(
674 user.id,
675 format!(
676 "invoked `update_user_is_deactivated` with x value `{}` and y value `{}`",
677 other_user.id, x
678 ),
679 ))
680 .await?;
681 }
682
683 Ok(())
685 }
686
687 pub async fn update_user_password(
688 &self,
689 id: usize,
690 from: String,
691 to: String,
692 user: User,
693 force: bool,
694 ) -> Result<()> {
695 if !user.check_password(from.clone()) && !force {
697 return Err(Error::MiscError("Password does not match".to_string()));
698 }
699
700 let conn = match self.0.connect().await {
702 Ok(c) => c,
703 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
704 };
705
706 let new_salt = salt();
707 let new_password = hash_salted(to, new_salt.clone());
708 let res = execute!(
709 &conn,
710 "UPDATE users SET password = $1, salt = $2 WHERE id = $3",
711 params![&new_password.as_str(), &new_salt.as_str(), &(id as i64)]
712 );
713
714 if let Err(e) = res {
715 return Err(Error::DatabaseError(e.to_string()));
716 }
717
718 self.cache_clear_user(&user).await;
719 Ok(())
720 }
721
722 pub async fn update_user_username(&self, id: usize, to: String, user: User) -> Result<()> {
723 if to.len() < 2 {
725 return Err(Error::DataTooShort("username".to_string()));
726 } else if to.len() > 32 {
727 return Err(Error::DataTooLong("username".to_string()));
728 }
729
730 if self.0.0.banned_usernames.contains(&to) {
731 return Err(Error::MiscError("This username cannot be used".to_string()));
732 }
733
734 let regex = regex::RegexBuilder::new(r"[^\w_\-\.!]+")
735 .multi_line(true)
736 .build()
737 .unwrap();
738
739 if regex.captures(&to).is_some() {
740 return Err(Error::MiscError(
741 "This username contains invalid characters".to_string(),
742 ));
743 }
744
745 let conn = match self.0.connect().await {
747 Ok(c) => c,
748 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
749 };
750
751 let res = execute!(
752 &conn,
753 "UPDATE users SET username = $1 WHERE id = $2",
754 params![&to.to_lowercase(), &(id as i64)]
755 );
756
757 if let Err(e) = res {
758 return Err(Error::DatabaseError(e.to_string()));
759 }
760
761 self.cache_clear_user(&user).await;
762 Ok(())
763 }
764
765 pub async fn update_user_awaiting_purchased_status(
766 &self,
767 id: usize,
768 x: bool,
769 user: User,
770 require_permission: bool,
771 ) -> Result<()> {
772 if (user.id != id) | require_permission
773 && !user.permissions.check(FinePermission::MANAGE_USERS)
774 {
775 return Err(Error::NotAllowed);
776 }
777
778 let other_user = self.get_user_by_id(id).await?;
779
780 let conn = match self.0.connect().await {
781 Ok(c) => c,
782 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
783 };
784
785 let res = execute!(
786 &conn,
787 "UPDATE users SET awaiting_purchase = $1 WHERE id = $2",
788 params![&{ if x { 1 } else { 0 } }, &(id as i64)]
789 );
790
791 if let Err(e) = res {
792 return Err(Error::DatabaseError(e.to_string()));
793 }
794
795 self.cache_clear_user(&other_user).await;
796
797 if user.id != other_user.id {
799 self.create_audit_log_entry(AuditLogEntry::new(
800 user.id,
801 format!(
802 "invoked `update_user_purchased_status` with x value `{}` and y value `{}`",
803 other_user.id, x
804 ),
805 ))
806 .await?;
807 }
808
809 Ok(())
811 }
812
813 pub async fn seen_user(&self, user: &User) -> Result<()> {
814 let conn = match self.0.connect().await {
815 Ok(c) => c,
816 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
817 };
818
819 let res = execute!(
820 &conn,
821 "UPDATE users SET last_seen = $1 WHERE id = $2",
822 params![&(unix_epoch_timestamp() as i64), &(user.id as i64)]
823 );
824
825 if let Err(e) = res {
826 return Err(Error::DatabaseError(e.to_string()));
827 }
828
829 self.cache_clear_user(user).await;
830
831 Ok(())
832 }
833
834 #[async_recursion::async_recursion]
838 pub async fn add_achievement(
839 &self,
840 user: &mut User,
841 achievement: Achievement,
842 check_for_final: bool,
843 ) -> Result<()> {
844 if user.settings.disable_achievements {
845 return Ok(());
846 }
847
848 if user.achievements.iter().any(|x| x.name == achievement.name) {
849 return Ok(());
850 }
851
852 self.create_notification(Notification::new(
854 "You've earned a new achievement!".to_string(),
855 format!(
856 "You've earned the \"{}\" [achievement](/achievements)!",
857 achievement.name.title()
858 ),
859 user.id,
860 ))
861 .await?;
862
863 user.achievements.push(achievement);
865 self.update_user_achievements(user.id, user.achievements.to_owned())
866 .await?;
867
868 if check_for_final && user.achievements.len() + 1 == ACHIEVEMENTS {
870 self.add_achievement(user, AchievementName::GetAllOtherAchievements.into(), false)
871 .await?;
872 }
873
874 Ok(())
876 }
877
878 pub fn fill_achievements(
883 &self,
884 mut list: Vec<Achievement>,
885 ) -> Vec<(String, String, AchievementRarity, Achievement)> {
886 let mut out = Vec::new();
887
888 list.sort_by(|a, b| a.unlocked.cmp(&b.unlocked));
890 list.reverse();
891
892 for x in list {
894 out.push((
895 x.name.title().to_string(),
896 x.name.description().to_string(),
897 x.name.rarity(),
898 x,
899 ))
900 }
901
902 out
903 }
904
905 pub fn check_totp(&self, ua: &User, code: &str) -> bool {
907 let totp = ua.totp(Some(
908 self.0
909 .0
910 .host
911 .replace("http://", "")
912 .replace("https://", "")
913 .replace(":", "_"),
914 ));
915
916 if let Some(totp) = totp {
917 return !code.is_empty()
918 && (totp.check_current(code).unwrap()
919 | ua.recovery_codes.contains(&code.to_string()));
920 }
921
922 true
923 }
924
925 pub fn generate_totp_recovery_codes() -> Vec<String> {
927 let mut out: Vec<String> = Vec::new();
928
929 for _ in 0..9 {
930 out.push(salt())
931 }
932
933 out
934 }
935
936 pub async fn update_user_totp(
943 &self,
944 id: usize,
945 secret: &str,
946 recovery: &Vec<String>,
947 ) -> Result<()> {
948 let user = self.get_user_by_id(id).await?;
949
950 let conn = match self.0.connect().await {
952 Ok(c) => c,
953 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
954 };
955
956 let res = execute!(
957 &conn,
958 "UPDATE users SET totp = $1, recovery_codes = $2 WHERE id = $3",
959 params![
960 &secret,
961 &serde_json::to_string(recovery).unwrap(),
962 &(id as i64)
963 ]
964 );
965
966 if let Err(e) = res {
967 return Err(Error::DatabaseError(e.to_string()));
968 }
969
970 self.cache_clear_user(&user).await;
971 Ok(())
972 }
973
974 pub async fn enable_totp(
983 &self,
984 id: usize,
985 user: User,
986 ) -> Result<(String, String, Vec<String>)> {
987 let other_user = self.get_user_by_id(id).await?;
988
989 if other_user.id != user.id {
990 if other_user.permissions.check(FinePermission::MANAGE_USERS) {
991 self.create_audit_log_entry(AuditLogEntry::new(
993 user.id,
994 format!("invoked `enable_totp` with x value `{}`", other_user.id,),
995 ))
996 .await?;
997 } else {
998 return Err(Error::NotAllowed);
999 }
1000 }
1001
1002 let secret = totp_rs::Secret::default().to_string();
1003 let recovery = Self::generate_totp_recovery_codes();
1004 self.update_user_totp(id, &secret, &recovery).await?;
1005
1006 let other_user = self.get_user_by_id(id).await?;
1008
1009 let totp = other_user.totp(Some(
1011 self.0
1012 .0
1013 .host
1014 .replace("http://", "")
1015 .replace("https://", "")
1016 .replace(":", "_"),
1017 ));
1018
1019 if totp.is_none() {
1020 return Err(Error::MiscError("Failed to get TOTP code".to_string()));
1021 }
1022
1023 let totp = totp.unwrap();
1024
1025 let qr = match totp.get_qr_base64() {
1027 Ok(q) => q,
1028 Err(e) => return Err(Error::MiscError(e.to_string())),
1029 };
1030
1031 Ok((totp.get_secret_base32(), qr, recovery))
1033 }
1034
1035 pub async fn cache_clear_user(&self, user: &User) {
1036 self.0.1.remove(format!("atto.user:{}", user.id)).await;
1037 self.0
1038 .1
1039 .remove(format!("atto.user:{}", user.username))
1040 .await;
1041 }
1042
1043 update_role_fn!(update_user_role, FinePermission, "permissions");
1044 update_role_fn!(
1045 update_user_secondary_role,
1046 SecondaryPermission,
1047 "secondary_permissions"
1048 );
1049
1050 auto_method!(update_user_tokens(Vec<Token>)@get_user_by_id -> "UPDATE users SET tokens = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_user);
1051 auto_method!(update_user_grants(Vec<AuthGrant>)@get_user_by_id -> "UPDATE users SET grants = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_user);
1052 auto_method!(update_user_settings(UserSettings)@get_user_by_id -> "UPDATE users SET settings = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_user);
1053 auto_method!(update_user_connections(UserConnections)@get_user_by_id -> "UPDATE users SET connections = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_user);
1054 auto_method!(update_user_associated(Vec<usize>)@get_user_by_id -> "UPDATE users SET associated = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_user);
1055 auto_method!(update_user_achievements(Vec<Achievement>)@get_user_by_id -> "UPDATE users SET achievements = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_user);
1056 auto_method!(update_user_invite_code(i64)@get_user_by_id -> "UPDATE users SET invite_code = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_user);
1057 auto_method!(update_user_browser_session(&str)@get_user_by_id -> "UPDATE users SET browser_session = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_user);
1058 auto_method!(update_user_ban_reason(&str)@get_user_by_id -> "UPDATE users SET ban_reason = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_user);
1059 auto_method!(update_user_channel_mutes(Vec<usize>)@get_user_by_id -> "UPDATE users SET channel_mutes = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_user);
1060 auto_method!(update_user_ban_expire(i64)@get_user_by_id -> "UPDATE users SET ban_expire = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_user);
1061 auto_method!(update_user_checkouts(Vec<String>)@get_user_by_id -> "UPDATE users SET checkouts = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_user);
1062 auto_method!(update_user_last_policy_consent(i64)@get_user_by_id -> "UPDATE users SET last_policy_consent = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_user);
1063 auto_method!(update_user_close_friends_stack(i64)@get_user_by_id -> "UPDATE users SET close_friends_stack = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_user);
1064 auto_method!(update_user_shrimpcamp_link(i64)@get_user_by_id -> "UPDATE users SET shrimpcamp_link = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_user);
1065
1066 auto_method!(get_user_by_stripe_id(&str)@get_user_from_row -> "SELECT * FROM users WHERE stripe_id = $1" --name="user" --returns=User);
1067 auto_method!(update_user_stripe_id(&str)@get_user_by_id -> "UPDATE users SET stripe_id = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_user);
1068
1069 auto_method!(update_user_notification_count(i32)@get_user_by_id -> "UPDATE users SET notification_count = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_user);
1070 auto_method!(incr_user_notifications()@get_user_by_id -> "UPDATE users SET notification_count = notification_count + 1 WHERE id = $1" --cache-key-tmpl=cache_clear_user --incr);
1071 auto_method!(decr_user_notifications()@get_user_by_id -> "UPDATE users SET notification_count = notification_count - 1 WHERE id = $1" --cache-key-tmpl=cache_clear_user --decr=notification_count);
1072
1073 auto_method!(incr_user_follower_count()@get_user_by_id -> "UPDATE users SET follower_count = follower_count + 1 WHERE id = $1" --cache-key-tmpl=cache_clear_user --incr);
1074 auto_method!(decr_user_follower_count()@get_user_by_id -> "UPDATE users SET follower_count = follower_count - 1 WHERE id = $1" --cache-key-tmpl=cache_clear_user --decr=follower_count);
1075
1076 auto_method!(incr_user_following_count()@get_user_by_id -> "UPDATE users SET following_count = following_count + 1 WHERE id = $1" --cache-key-tmpl=cache_clear_user --incr);
1077 auto_method!(decr_user_following_count()@get_user_by_id -> "UPDATE users SET following_count = following_count - 1 WHERE id = $1" --cache-key-tmpl=cache_clear_user --decr=following_count);
1078
1079 auto_method!(incr_user_post_count()@get_user_by_id -> "UPDATE users SET post_count = post_count + 1 WHERE id = $1" --cache-key-tmpl=cache_clear_user --incr);
1080 auto_method!(decr_user_post_count()@get_user_by_id -> "UPDATE users SET post_count = post_count - 1 WHERE id = $1" --cache-key-tmpl=cache_clear_user --decr=post_count);
1081
1082 auto_method!(update_user_request_count(i32)@get_user_by_id -> "UPDATE users SET request_count = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_user);
1083 auto_method!(incr_user_request_count()@get_user_by_id -> "UPDATE users SET request_count = request_count + 1 WHERE id = $1" --cache-key-tmpl=cache_clear_user --incr);
1084 auto_method!(decr_user_request_count()@get_user_by_id -> "UPDATE users SET request_count = request_count - 1 WHERE id = $1" --cache-key-tmpl=cache_clear_user --decr=request_count);
1085
1086 auto_method!(get_user_by_invite_code(i64)@get_user_from_row -> "SELECT * FROM users WHERE invite_code = $1" --name="user" --returns=User);
1087
1088 auto_method!(update_user_missed_messages_count(i32)@get_user_by_id -> "UPDATE users SET missed_messages_count = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_user);
1089 auto_method!(incr_user_missed_messages()@get_user_by_id -> "UPDATE users SET missed_messages_count = missed_messages_count + 1 WHERE id = $1" --cache-key-tmpl=cache_clear_user --incr);
1090 auto_method!(decr_user_missed_messages()@get_user_by_id -> "UPDATE users SET missed_messages_count = missed_messages_count - 1 WHERE id = $1" --cache-key-tmpl=cache_clear_user --decr=notification_count);
1091
1092 auto_method!(incr_profile_views()@get_user_by_id -> "UPDATE users SET views = views + 1 WHERE id = $1" --cache-key-tmpl=cache_clear_user --incr);
1093 auto_method!(decr_profile_views()@get_user_by_id -> "UPDATE users SET views = views - 1 WHERE id = $1" --cache-key-tmpl=cache_clear_user --decr=views);
1094}