X-Git-Url: http://these/git/?a=blobdiff_plain;f=crates%2Fapi%2Fsrc%2Flib.rs;h=b297f503f6f9d4abc2115255a55e8ca567bd5fff;hb=3471f3533cb724b2cf6953d563aadfcc9f66c1d2;hp=595ab10f8059d3ac19781d8b6332e99b54d4182c;hpb=235cc8b22897bfb3e71ba3dbd725d36863fea8ba;p=lemmy.git diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs index 595ab10f..b297f503 100644 --- a/crates/api/src/lib.rs +++ b/crates/api/src/lib.rs @@ -1,19 +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::*, - private_message::*, - site::*, - utils::local_site_to_slur_regex, - websocket::*, -}; +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, utils::check_slurs, ConnectionId}; -use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation}; -use serde::Deserialize; +use lemmy_utils::{ + error::{LemmyError, LemmyErrorExt, LemmyErrorType}, + utils::slurs::check_slurs, +}; +use std::io::Cursor; mod comment; mod comment_report; @@ -24,226 +18,67 @@ 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; - - async fn perform( - &self, - context: &Data, - websocket_id: Option, - ) -> 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::MarkCommentReplyAsRead => { - 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 - } - UserOperation::CreatePrivateMessageReport => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::ResolvePrivateMessageReport => { - do_websocket_operation::(context, id, op, data).await - } - UserOperation::ListPrivateMessageReports => { - do_websocket_operation::(context, id, op, data).await - } - - // Site ops - UserOperation::GetModlog => 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, + type Response: serde::ser::Serialize + Send + Clone + Sync; - // 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 - } - - // Comment ops - 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 - } - } -} - -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) + async fn perform(&self, context: &Data) -> Result; } /// Converts the captcha to a base64 encoded wav audio file -pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> String { +pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> Result { let letters = captcha.as_wav(); - let mut concat_letters: Vec = Vec::new(); - + // 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 bytes = letter.unwrap_or_default(); - concat_letters.extend(bytes); + 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)?; + } } - // Convert to base64 - base64::encode(concat_letters) + // 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())) } -/// Check size of report and remove whitespace +/// Check size of report pub(crate) fn check_report_reason(reason: &str, local_site: &LocalSite) -> Result<(), LemmyError> { let slur_regex = &local_site_to_slur_regex(local_site); check_slurs(reason, slur_regex)?; if reason.is_empty() { - return Err(LemmyError::from_message("report_reason_required")); + return Err(LemmyErrorType::ReportReasonRequired)?; } if reason.chars().count() > 1000 { - return Err(LemmyError::from_message("report_too_long")); + 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::{ @@ -253,17 +88,22 @@ mod tests { secret::Secret, }, traits::Crud, - utils::establish_unpooled_connection, + utils::build_db_pool_for_tests, }; use lemmy_utils::{claims::Claims, settings::SETTINGS}; - - #[test] - fn test_should_not_validate_user_token_after_password_change() { - let conn = &mut establish_unpooled_connection(); - let secret = Secret::init(conn).unwrap(); + 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(); - let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()) + .await + .unwrap(); let new_person = PersonInsertForm::builder() .name("Gerry9812".into()) @@ -271,14 +111,14 @@ mod tests { .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 = 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, @@ -292,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); } }