]> Untitled Git - lemmy.git/blob - crates/api/src/lib.rs
feat: re-added captcha checks (#3249)
[lemmy.git] / crates / api / src / lib.rs
1 use actix_web::web::Data;
2 use captcha::Captcha;
3 use lemmy_api_common::{context::LemmyContext, utils::local_site_to_slur_regex};
4 use lemmy_db_schema::source::local_site::LocalSite;
5 use lemmy_utils::{error::LemmyError, utils::slurs::check_slurs};
6
7 mod comment;
8 mod comment_report;
9 mod community;
10 mod local_user;
11 mod post;
12 mod post_report;
13 mod private_message;
14 mod private_message_report;
15 mod site;
16
17 #[async_trait::async_trait(?Send)]
18 pub trait Perform {
19   type Response: serde::ser::Serialize + Send;
20
21   async fn perform(&self, context: &Data<LemmyContext>) -> Result<Self::Response, LemmyError>;
22 }
23
24 /// Converts the captcha to a base64 encoded wav audio file
25 pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> String {
26   let letters = captcha.as_wav();
27
28   let mut concat_letters: Vec<u8> = Vec::new();
29
30   for letter in letters {
31     let bytes = letter.unwrap_or_default();
32     concat_letters.extend(bytes);
33   }
34
35   // Convert to base64
36   base64::encode(concat_letters)
37 }
38
39 /// Check size of report and remove whitespace
40 pub(crate) fn check_report_reason(reason: &str, local_site: &LocalSite) -> Result<(), LemmyError> {
41   let slur_regex = &local_site_to_slur_regex(local_site);
42
43   check_slurs(reason, slur_regex)?;
44   if reason.is_empty() {
45     return Err(LemmyError::from_message("report_reason_required"));
46   }
47   if reason.chars().count() > 1000 {
48     return Err(LemmyError::from_message("report_too_long"));
49   }
50   Ok(())
51 }
52
53 #[cfg(test)]
54 mod tests {
55   use lemmy_api_common::utils::check_validator_time;
56   use lemmy_db_schema::{
57     source::{
58       instance::Instance,
59       local_user::{LocalUser, LocalUserInsertForm},
60       person::{Person, PersonInsertForm},
61       secret::Secret,
62     },
63     traits::Crud,
64     utils::build_db_pool_for_tests,
65   };
66   use lemmy_utils::{claims::Claims, settings::SETTINGS};
67   use serial_test::serial;
68
69   #[tokio::test]
70   #[serial]
71   async fn test_should_not_validate_user_token_after_password_change() {
72     let pool = &build_db_pool_for_tests().await;
73     let secret = Secret::init(pool).await.unwrap();
74     let settings = &SETTINGS.to_owned();
75
76     let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
77       .await
78       .unwrap();
79
80     let new_person = PersonInsertForm::builder()
81       .name("Gerry9812".into())
82       .public_key("pubkey".to_string())
83       .instance_id(inserted_instance.id)
84       .build();
85
86     let inserted_person = Person::create(pool, &new_person).await.unwrap();
87
88     let local_user_form = LocalUserInsertForm::builder()
89       .person_id(inserted_person.id)
90       .password_encrypted("123456".to_string())
91       .build();
92
93     let inserted_local_user = LocalUser::create(pool, &local_user_form).await.unwrap();
94
95     let jwt = Claims::jwt(
96       inserted_local_user.id.0,
97       &secret.jwt_secret,
98       &settings.hostname,
99     )
100     .unwrap();
101     let claims = Claims::decode(&jwt, &secret.jwt_secret).unwrap().claims;
102     let check = check_validator_time(&inserted_local_user.validator_time, &claims);
103     assert!(check.is_ok());
104
105     // The check should fail, since the validator time is now newer than the jwt issue time
106     let updated_local_user =
107       LocalUser::update_password(pool, inserted_local_user.id, "password111")
108         .await
109         .unwrap();
110     let check_after = check_validator_time(&updated_local_user.validator_time, &claims);
111     assert!(check_after.is_err());
112
113     let num_deleted = Person::delete(pool, inserted_person.id).await.unwrap();
114     assert_eq!(1, num_deleted);
115   }
116 }