X-Git-Url: http://these/git/?a=blobdiff_plain;f=crates%2Fapi%2Fsrc%2Flib.rs;h=9d3cf211c233ef5da1df63408310b18cb69ef840;hb=92568956353f21649ed9aff68b42699c9d036f30;hp=5083b2867afd67bee14284ef42ed3bcb1a5a3b74;hpb=4e12e25c59beef296c750fec640b0b80c4c11de9;p=lemmy.git diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs index 5083b286..9d3cf211 100644 --- a/crates/api/src/lib.rs +++ b/crates/api/src/lib.rs @@ -1,9 +1,13 @@ -use actix_web::{web, web::Data}; +use actix_web::web::Data; +use base64::{engine::general_purpose::STANDARD_NO_PAD as base64, Engine}; use captcha::Captcha; -use lemmy_api_common::{comment::*, community::*, person::*, post::*, site::*, websocket::*}; -use lemmy_utils::{error::LemmyError, ConnectionId}; -use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation}; -use serde::Deserialize; +use lemmy_api_common::{context::LemmyContext, utils::local_site_to_slur_regex}; +use lemmy_db_schema::source::local_site::LocalSite; +use lemmy_utils::{ + error::{LemmyError, LemmyErrorExt, LemmyErrorType}, + utils::slurs::check_slurs, +}; +use std::io::Cursor; mod comment; mod comment_report; @@ -12,242 +16,109 @@ mod local_user; mod post; mod post_report; mod private_message; +mod private_message_report; mod site; -mod websocket; #[async_trait::async_trait(?Send)] pub trait Perform { - type Response: serde::ser::Serialize + Send; + type Response: serde::ser::Serialize + Send + Clone + Sync; - async fn perform( - &self, - context: &Data, - websocket_id: Option, - ) -> Result; + async fn perform(&self, context: &Data) -> Result; } -pub async fn match_websocket_operation( - context: LemmyContext, - id: ConnectionId, - op: UserOperation, - data: &str, -) -> Result { - match op { - // User ops - UserOperation::Login => do_websocket_operation::(context, id, op, data).await, - UserOperation::GetCaptcha => do_websocket_operation::(context, id, op, data).await, - UserOperation::GetReplies => do_websocket_operation::(context, id, op, data).await, - UserOperation::AddAdmin => do_websocket_operation::(context, id, op, data).await, - UserOperation::GetUnreadRegistrationApplicationCount => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::ListRegistrationApplications => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::ApproveRegistrationApplication => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::BanPerson => do_websocket_operation::(context, id, op, data).await, - UserOperation::GetBannedPersons => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::BlockPerson => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::GetPersonMentions => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::MarkPersonMentionAsRead => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::MarkAllAsRead => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::PasswordReset => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::PasswordChange => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::UserJoin => do_websocket_operation::(context, id, op, data).await, - UserOperation::PostJoin => do_websocket_operation::(context, id, op, data).await, - UserOperation::CommunityJoin => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::ModJoin => do_websocket_operation::(context, id, op, data).await, - UserOperation::SaveUserSettings => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::ChangePassword => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::GetReportCount => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::GetUnreadCount => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::VerifyEmail => { - do_websocket_operation::(context, id, op, data).await - } - - // Private Message ops - UserOperation::MarkPrivateMessageAsRead => { - do_websocket_operation::(context, id, op, data).await - } - - // Site ops - UserOperation::GetModlog => do_websocket_operation::(context, id, op, data).await, - UserOperation::GetSiteConfig => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::SaveSiteConfig => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::PurgePerson => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::PurgeCommunity => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::PurgePost => do_websocket_operation::(context, id, op, data).await, - UserOperation::PurgeComment => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::Search => do_websocket_operation::(context, id, op, data).await, - UserOperation::ResolveObject => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::TransferCommunity => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::LeaveAdmin => do_websocket_operation::(context, id, op, data).await, - - // Community ops - UserOperation::FollowCommunity => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::BlockCommunity => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::BanFromCommunity => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::AddModToCommunity => { - do_websocket_operation::(context, id, op, data).await - } - - // Post ops - UserOperation::LockPost => do_websocket_operation::(context, id, op, data).await, - UserOperation::StickyPost => do_websocket_operation::(context, id, op, data).await, - UserOperation::CreatePostLike => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::MarkPostAsRead => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::SavePost => do_websocket_operation::(context, id, op, data).await, - UserOperation::CreatePostReport => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::ListPostReports => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::ResolvePostReport => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::GetSiteMetadata => { - do_websocket_operation::(context, id, op, data).await - } +/// Converts the captcha to a base64 encoded wav audio file +pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> Result { + let letters = captcha.as_wav(); - // Comment ops - UserOperation::MarkCommentAsRead => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::SaveComment => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::CreateCommentLike => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::CreateCommentReport => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::ListCommentReports => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::ResolveCommentReport => { - do_websocket_operation::(context, id, op, data).await + // Decode each wav file, concatenate the samples + let mut concat_samples: Vec = Vec::new(); + let mut any_header: Option = None; + for letter in letters { + let mut cursor = Cursor::new(letter.unwrap_or_default()); + let (header, samples) = wav::read(&mut cursor)?; + any_header = Some(header); + if let Some(samples16) = samples.as_sixteen() { + concat_samples.extend(samples16); + } else { + return Err(LemmyErrorType::CouldntCreateAudioCaptcha)?; } } -} -async fn do_websocket_operation<'a, 'b, Data>( - context: LemmyContext, - id: ConnectionId, - op: UserOperation, - data: &str, -) -> Result -where - for<'de> Data: Deserialize<'de> + 'a, - Data: Perform, -{ - let parsed_data: Data = serde_json::from_str(data)?; - let res = parsed_data - .perform(&web::Data::new(context), Some(id)) - .await?; - serialize_websocket_message(&op, &res) + // Encode the concatenated result as a wav file + let mut output_buffer = Cursor::new(vec![]); + let header = match any_header { + Some(header) => header, + None => return Err(LemmyErrorType::CouldntCreateAudioCaptcha)?, + }; + wav::write( + header, + &wav::BitDepth::Sixteen(concat_samples), + &mut output_buffer, + ) + .with_lemmy_type(LemmyErrorType::CouldntCreateAudioCaptcha)?; + + Ok(base64.encode(output_buffer.into_inner())) } -/// Converts the captcha to a base64 encoded wav audio file -pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> String { - let letters = captcha.as_wav(); - - let mut concat_letters: Vec = Vec::new(); +/// Check size of report and remove whitespace +pub(crate) fn check_report_reason(reason: &str, local_site: &LocalSite) -> Result<(), LemmyError> { + let slur_regex = &local_site_to_slur_regex(local_site); - for letter in letters { - let bytes = letter.unwrap_or_default(); - concat_letters.extend(bytes); + check_slurs(reason, slur_regex)?; + if reason.is_empty() { + return Err(LemmyErrorType::ReportReasonRequired)?; } - - // Convert to base64 - base64::encode(concat_letters) + if reason.chars().count() > 1000 { + return Err(LemmyErrorType::ReportTooLong)?; + } + Ok(()) } #[cfg(test)] mod tests { + #![allow(clippy::unwrap_used)] + #![allow(clippy::indexing_slicing)] + use lemmy_api_common::utils::check_validator_time; use lemmy_db_schema::{ source::{ - local_user::{LocalUser, LocalUserForm}, - person::{Person, PersonForm}, + instance::Instance, + local_user::{LocalUser, LocalUserInsertForm}, + person::{Person, PersonInsertForm}, secret::Secret, }, traits::Crud, - utils::establish_unpooled_connection, + utils::build_db_pool_for_tests, }; - use lemmy_utils::{claims::Claims, settings::structs::Settings}; + use lemmy_utils::{claims::Claims, settings::SETTINGS}; + use serial_test::serial; + + #[tokio::test] + #[serial] + async fn test_should_not_validate_user_token_after_password_change() { + let pool = &build_db_pool_for_tests().await; + let pool = &mut pool.into(); + let secret = Secret::init(pool).await.unwrap(); + let settings = &SETTINGS.to_owned(); - #[test] - fn test_should_not_validate_user_token_after_password_change() { - let conn = establish_unpooled_connection(); - let secret = Secret::init(&conn).unwrap(); - let settings = Settings::init().unwrap(); + let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()) + .await + .unwrap(); - let new_person = PersonForm { - name: "Gerry9812".into(), - ..PersonForm::default() - }; + let new_person = PersonInsertForm::builder() + .name("Gerry9812".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); - let inserted_person = Person::create(&conn, &new_person).unwrap(); + let inserted_person = Person::create(pool, &new_person).await.unwrap(); - let local_user_form = LocalUserForm { - person_id: Some(inserted_person.id), - password_encrypted: Some("123456".to_string()), - ..LocalUserForm::default() - }; + let local_user_form = LocalUserInsertForm::builder() + .person_id(inserted_person.id) + .password_encrypted("123456".to_string()) + .build(); - let inserted_local_user = LocalUser::create(&conn, &local_user_form).unwrap(); + let inserted_local_user = LocalUser::create(pool, &local_user_form).await.unwrap(); let jwt = Claims::jwt( inserted_local_user.id.0, @@ -261,11 +132,13 @@ mod tests { // The check should fail, since the validator time is now newer than the jwt issue time let updated_local_user = - LocalUser::update_password(&conn, inserted_local_user.id, "password111").unwrap(); + LocalUser::update_password(pool, inserted_local_user.id, "password111") + .await + .unwrap(); let check_after = check_validator_time(&updated_local_user.validator_time, &claims); assert!(check_after.is_err()); - let num_deleted = Person::delete(&conn, inserted_person.id).unwrap(); + let num_deleted = Person::delete(pool, inserted_person.id).await.unwrap(); assert_eq!(1, num_deleted); } }