From: Dessalines Date: Tue, 6 Apr 2021 16:09:26 +0000 (-0400) Subject: Merge branch 'main' into change_password_api X-Git-Url: http://these/git/?a=commitdiff_plain;h=d97129e7ae095ae39de2ce321eaaac2bfe1bdcb6;hp=-c;p=lemmy.git Merge branch 'main' into change_password_api --- d97129e7ae095ae39de2ce321eaaac2bfe1bdcb6 diff --combined crates/api/src/lib.rs index d25d0bbc,c9de749e..0ba3fbea --- a/crates/api/src/lib.rs +++ b/crates/api/src/lib.rs @@@ -1,9 -1,9 +1,9 @@@ use actix_web::{web, web::Data}; + use captcha::Captcha; use lemmy_api_common::{comment::*, community::*, person::*, post::*, site::*, websocket::*}; use lemmy_utils::{ConnectionId, LemmyError}; use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation}; use serde::Deserialize; - use std::{env, process::Command}; mod comment; mod comment_report; @@@ -63,9 -63,6 +63,9 @@@ pub async fn match_websocket_operation 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 } @@@ -161,60 -158,23 +161,23 @@@ wher serialize_websocket_message(&op, &res) } - pub(crate) fn captcha_espeak_wav_base64(captcha: &str) -> Result { - let mut built_text = String::new(); + /// 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(); - // Building proper speech text for espeak - for mut c in captcha.chars() { - let new_str = if c.is_alphabetic() { - if c.is_lowercase() { - c.make_ascii_uppercase(); - format!("lower case {} ... ", c) - } else { - c.make_ascii_uppercase(); - format!("capital {} ... ", c) - } - } else { - format!("{} ...", c) - }; + let mut concat_letters: Vec = Vec::new(); - built_text.push_str(&new_str); + for letter in letters { + let bytes = letter.unwrap_or_default(); + concat_letters.extend(bytes); } - espeak_wav_base64(&built_text) - } - - pub(crate) fn espeak_wav_base64(text: &str) -> Result { - // Make a temp file path - let uuid = uuid::Uuid::new_v4().to_string(); - let file_path = format!( - "{}/lemmy_espeak_{}.wav", - env::temp_dir().to_string_lossy(), - &uuid - ); - - // Write the wav file - Command::new("espeak") - .arg("-w") - .arg(&file_path) - .arg(text) - .status()?; - - // Read the wav file bytes - let bytes = std::fs::read(&file_path)?; - - // Delete the file - std::fs::remove_file(file_path)?; - // Convert to base64 - let base64 = base64::encode(bytes); - - Ok(base64) + base64::encode(concat_letters) } #[cfg(test)] mod tests { - use crate::captcha_espeak_wav_base64; use lemmy_api_common::check_validator_time; use lemmy_db_queries::{establish_unpooled_connection, source::local_user::LocalUser_, Crud}; use lemmy_db_schema::source::{ @@@ -256,9 -216,4 +219,4 @@@ let num_deleted = Person::delete(&conn, inserted_person.id).unwrap(); assert_eq!(1, num_deleted); } - - #[test] - fn test_espeak() { - assert!(captcha_espeak_wav_base64("WxRt2l").is_ok()) - } } diff --combined crates/api/src/local_user.rs index d1917149,6a319dcd..43954aa8 --- a/crates/api/src/local_user.rs +++ b/crates/api/src/local_user.rs @@@ -1,4 -1,4 +1,4 @@@ - use crate::{captcha_espeak_wav_base64, Perform}; + use crate::{captcha_as_wav_base64, Perform}; use actix_web::web::Data; use anyhow::Context; use bcrypt::verify; @@@ -60,7 -60,7 +60,7 @@@ use lemmy_utils:: email::send_email, location_info, settings::structs::Settings, - utils::{generate_random_string, is_valid_preferred_username, naive_from_unix}, + utils::{generate_random_string, is_valid_display_name, naive_from_unix}, ApiError, ConnectionId, LemmyError, @@@ -135,13 -135,11 +135,11 @@@ impl Perform for GetCaptcha let answer = captcha.chars_as_string(); - let png_byte_array = captcha.as_png().expect("failed to generate captcha"); - - let png = base64::encode(png_byte_array); + let png = captcha.as_base64().expect("failed to generate captcha"); let uuid = uuid::Uuid::new_v4().to_string(); - let wav = captcha_espeak_wav_base64(&answer).ok(); + let wav = captcha_as_wav_base64(&captcha); let captcha_item = CaptchaItem { answer, @@@ -174,7 -172,7 +172,7 @@@ impl Perform for SaveUserSettings let banner = diesel_option_overwrite_to_url(&data.banner)?; let email = diesel_option_overwrite(&data.email); let bio = diesel_option_overwrite(&data.bio); - let preferred_username = diesel_option_overwrite(&data.preferred_username); + let display_name = diesel_option_overwrite(&data.display_name); let matrix_user_id = diesel_option_overwrite(&data.matrix_user_id); if let Some(Some(bio)) = &bio { @@@ -183,24 -181,59 +181,24 @@@ } } - if let Some(Some(preferred_username)) = &preferred_username { - if !is_valid_preferred_username(preferred_username.trim()) { + if let Some(Some(display_name)) = &display_name { + if !is_valid_display_name(display_name.trim()) { return Err(ApiError::err("invalid_username").into()); } } let local_user_id = local_user_view.local_user.id; let person_id = local_user_view.person.id; - let password_encrypted = match &data.new_password { - Some(new_password) => { - match &data.new_password_verify { - Some(new_password_verify) => { - password_length_check(&new_password)?; - - // Make sure passwords match - if new_password != new_password_verify { - return Err(ApiError::err("passwords_dont_match").into()); - } - - // Check the old password - match &data.old_password { - Some(old_password) => { - let valid: bool = - verify(old_password, &local_user_view.local_user.password_encrypted) - .unwrap_or(false); - if !valid { - return Err(ApiError::err("password_incorrect").into()); - } - let new_password = new_password.to_owned(); - let user = blocking(context.pool(), move |conn| { - LocalUser::update_password(conn, local_user_id, &new_password) - }) - .await??; - user.password_encrypted - } - None => return Err(ApiError::err("password_incorrect").into()), - } - } - None => return Err(ApiError::err("passwords_dont_match").into()), - } - } - None => local_user_view.local_user.password_encrypted, - }; - let default_listing_type = data.default_listing_type; let default_sort_type = data.default_sort_type; + let password_encrypted = local_user_view.local_user.password_encrypted; let person_form = PersonForm { name: local_user_view.person.name, avatar, banner, inbox_url: None, - preferred_username, + display_name, published: None, updated: Some(naive_now()), banned: None, @@@ -232,6 -265,7 +230,7 @@@ email, password_encrypted, show_nsfw: data.show_nsfw, + show_scores: data.show_scores, theme: data.theme.to_owned(), default_sort_type, default_listing_type, @@@ -266,49 -300,6 +265,49 @@@ } } +#[async_trait::async_trait(?Send)] +impl Perform for ChangePassword { + type Response = LoginResponse; + + async fn perform( + &self, + context: &Data, + _websocket_id: Option, + ) -> Result { + let data: &ChangePassword = &self; + let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?; + + password_length_check(&data.new_password)?; + + // Make sure passwords match + if data.new_password != data.new_password_verify { + return Err(ApiError::err("passwords_dont_match").into()); + } + + // Check the old password + let valid: bool = verify( + &data.old_password, + &local_user_view.local_user.password_encrypted, + ) + .unwrap_or(false); + if !valid { + return Err(ApiError::err("password_incorrect").into()); + } + + let local_user_id = local_user_view.local_user.id; + let new_password = data.new_password.to_owned(); + let updated_local_user = blocking(context.pool(), move |conn| { + LocalUser::update_password(conn, local_user_id, &new_password) + }) + .await??; + + // Return the jwt + Ok(LoginResponse { + jwt: Claims::jwt(updated_local_user.id.0)?, + }) + } +} + #[async_trait::async_trait(?Send)] impl Perform for AddAdmin { type Response = AddAdminResponse; diff --combined crates/api_common/src/person.rs index 53033a92,21fa1c77..6402096c --- a/crates/api_common/src/person.rs +++ b/crates/api_common/src/person.rs @@@ -39,37 -39,33 +39,41 @@@ pub struct GetCaptchaResponse #[derive(Serialize)] pub struct CaptchaResponse { - pub png: String, // A Base64 encoded png - pub wav: Option, // A Base64 encoded wav audio + pub png: String, // A Base64 encoded png + pub wav: String, // A Base64 encoded wav audio pub uuid: String, } #[derive(Deserialize)] pub struct SaveUserSettings { pub show_nsfw: Option, + pub show_scores: Option, - pub show_avatars: Option, pub theme: Option, pub default_sort_type: Option, pub default_listing_type: Option, pub lang: Option, pub avatar: Option, pub banner: Option, - pub preferred_username: Option, + pub display_name: Option, pub email: Option, pub bio: Option, pub matrix_user_id: Option, + pub show_avatars: Option, + pub new_password: Option, + pub new_password_verify: Option, + pub old_password: Option, pub send_notifications_to_email: Option, pub auth: String, } +#[derive(Deserialize)] +pub struct ChangePassword { + pub new_password: String, + pub new_password_verify: String, + pub old_password: String, + pub auth: String, +} + #[derive(Serialize)] pub struct LoginResponse { pub jwt: String,