diff --git a/src/activitypub/actors/helpers.rs b/src/activitypub/actors/helpers.rs index 268d535..92e3a86 100644 --- a/src/activitypub/actors/helpers.rs +++ b/src/activitypub/actors/helpers.rs @@ -21,7 +21,10 @@ use crate::models::{ ProfileUpdateData, }, }; -use crate::validators::posts::EMOJIS_MAX_NUM; +use crate::validators::{ + posts::EMOJIS_MAX_NUM, + profiles::{clean_profile_create_data, clean_profile_update_data}, +}; pub const ACTOR_IMAGE_MAX_SIZE: usize = 5 * 1000 * 1000; // 5 MB @@ -173,7 +176,7 @@ pub async fn create_remote_profile( emojis, actor_json: Some(actor), }; - profile_data.clean()?; + clean_profile_create_data(&mut profile_data)?; let profile = create_profile(db_client, profile_data).await?; Ok(profile) } @@ -231,7 +234,7 @@ pub async fn update_remote_profile( emojis, actor_json: Some(actor), }; - profile_data.clean()?; + clean_profile_update_data(&mut profile_data)?; let profile = update_profile(db_client, &profile.id, profile_data).await?; Ok(profile) } diff --git a/src/mastodon_api/accounts/views.rs b/src/mastodon_api/accounts/views.rs index d275ee6..d9aa09a 100644 --- a/src/mastodon_api/accounts/views.rs +++ b/src/mastodon_api/accounts/views.rs @@ -99,6 +99,7 @@ use crate::models::{ }, users::types::{Role, UserCreateData}, }; +use crate::validators::profiles::clean_profile_update_data; use super::helpers::{follow_or_create_request, get_relationship}; use super::types::{ Account, @@ -246,7 +247,7 @@ async fn update_credentials( ¤t_user.profile, &config.media_dir(), )?; - profile_data.clean()?; + clean_profile_update_data(&mut profile_data)?; current_user.profile = update_profile( db_client, ¤t_user.id, diff --git a/src/models/profiles/types.rs b/src/models/profiles/types.rs index 4b0a41c..10a5716 100644 --- a/src/models/profiles/types.rs +++ b/src/models/profiles/types.rs @@ -20,14 +20,7 @@ use crate::database::{ json_macro::{json_from_sql, json_to_sql}, DatabaseTypeError, }; -use crate::errors::ValidationError; use crate::models::emojis::types::DbEmoji; -use crate::validators::profiles::{ - validate_username, - validate_display_name, - clean_bio, - clean_extra_fields, -}; #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ProfileImage { @@ -456,25 +449,6 @@ pub struct ProfileCreateData { pub actor_json: Option, } -impl ProfileCreateData { - pub fn clean(&mut self) -> Result<(), ValidationError> { - validate_username(&self.username)?; - if self.hostname.is_some() != self.actor_json.is_some() { - return Err(ValidationError("hostname and actor_json field mismatch")); - }; - if let Some(display_name) = &self.display_name { - validate_display_name(display_name)?; - }; - let is_remote = self.actor_json.is_some(); - if let Some(bio) = &self.bio { - let cleaned_bio = clean_bio(bio, is_remote)?; - self.bio = Some(cleaned_bio); - }; - self.extra_fields = clean_extra_fields(&self.extra_fields, is_remote)?; - Ok(()) - } -} - pub struct ProfileUpdateData { pub display_name: Option, pub bio: Option, @@ -506,20 +480,6 @@ impl ProfileUpdateData { }); self.payment_options.push(option); } - - pub fn clean(&mut self) -> Result<(), ValidationError> { - if let Some(display_name) = &self.display_name { - validate_display_name(display_name)?; - }; - let is_remote = self.actor_json.is_some(); - // Validate and clean bio - if let Some(bio) = &self.bio { - let cleaned_bio = clean_bio(bio, is_remote)?; - self.bio = Some(cleaned_bio); - }; - self.extra_fields = clean_extra_fields(&self.extra_fields, is_remote)?; - Ok(()) - } } impl From<&DbActorProfile> for ProfileUpdateData { @@ -546,7 +506,6 @@ impl From<&DbActorProfile> for ProfileUpdateData { #[cfg(test)] mod tests { - use crate::activitypub::actors::types::Actor; use super::*; #[test] @@ -588,20 +547,4 @@ mod tests { let serialized = serde_json::to_string(&payment_option).unwrap(); assert_eq!(serialized, r#"{"payment_type":2,"chain_id":"eip155:1"}"#); } - - #[test] - fn test_clean_profile_create_data() { - let mut profile_data = ProfileCreateData { - username: "test".to_string(), - hostname: Some("example.org".to_string()), - display_name: Some("Test Test".to_string()), - actor_json: Some(Actor { - id: "https://example.org/test".to_string(), - ..Default::default() - }), - ..Default::default() - }; - let result = profile_data.clean(); - assert_eq!(result.is_ok(), true); - } } diff --git a/src/validators/profiles.rs b/src/validators/profiles.rs index ede6cfc..649e39c 100644 --- a/src/validators/profiles.rs +++ b/src/validators/profiles.rs @@ -3,7 +3,11 @@ use regex::Regex; use mitra_utils::html::{clean_html, clean_html_strict}; use crate::errors::ValidationError; -use crate::models::profiles::types::ExtraField; +use crate::models::profiles::types::{ + ExtraField, + ProfileCreateData, + ProfileUpdateData, +}; const USERNAME_RE: &str = r"^[a-zA-Z0-9_\.-]+$"; const DISPLAY_NAME_MAX_LENGTH: usize = 200; @@ -26,7 +30,7 @@ pub fn validate_username(username: &str) -> Result<(), ValidationError> { Ok(()) } -pub fn validate_display_name(display_name: &str) +fn validate_display_name(display_name: &str) -> Result<(), ValidationError> { if display_name.chars().count() > DISPLAY_NAME_MAX_LENGTH { @@ -35,7 +39,7 @@ pub fn validate_display_name(display_name: &str) Ok(()) } -pub fn clean_bio(bio: &str, is_remote: bool) -> Result { +fn clean_bio(bio: &str, is_remote: bool) -> Result { let cleaned_bio = if is_remote { // Remote profile let truncated_bio: String = bio.chars().take(BIO_MAX_LENGTH).collect(); @@ -51,7 +55,7 @@ pub fn clean_bio(bio: &str, is_remote: bool) -> Result } /// Validates extra fields and removes fields with empty labels -pub fn clean_extra_fields( +fn clean_extra_fields( extra_fields: &[ExtraField], is_remote: bool, ) -> Result, ValidationError> { @@ -83,8 +87,49 @@ pub fn clean_extra_fields( Ok(cleaned_extra_fields) } +pub fn clean_profile_create_data( + profile_data: &mut ProfileCreateData, +) -> Result<(), ValidationError> { + validate_username(&profile_data.username)?; + if profile_data.hostname.is_some() != profile_data.actor_json.is_some() { + return Err(ValidationError("hostname and actor_json field mismatch")); + }; + if let Some(display_name) = &profile_data.display_name { + validate_display_name(display_name)?; + }; + let is_remote = profile_data.actor_json.is_some(); + if let Some(bio) = &profile_data.bio { + let cleaned_bio = clean_bio(bio, is_remote)?; + profile_data.bio = Some(cleaned_bio); + }; + profile_data.extra_fields = clean_extra_fields( + &profile_data.extra_fields, + is_remote, + )?; + Ok(()) +} + +pub fn clean_profile_update_data( + profile_data: &mut ProfileUpdateData, +) -> Result<(), ValidationError> { + if let Some(display_name) = &profile_data.display_name { + validate_display_name(display_name)?; + }; + let is_remote = profile_data.actor_json.is_some(); + if let Some(bio) = &profile_data.bio { + let cleaned_bio = clean_bio(bio, is_remote)?; + profile_data.bio = Some(cleaned_bio); + }; + profile_data.extra_fields = clean_extra_fields( + &profile_data.extra_fields, + is_remote, + )?; + Ok(()) +} + #[cfg(test)] mod tests { + use crate::activitypub::actors::types::Actor; use super::*; #[test] @@ -133,4 +178,20 @@ mod tests { assert_eq!(result.name, "$ETH"); assert_eq!(result.value, "0x1234"); } + + #[test] + fn test_clean_profile_create_data() { + let mut profile_data = ProfileCreateData { + username: "test".to_string(), + hostname: Some("example.org".to_string()), + display_name: Some("Test Test".to_string()), + actor_json: Some(Actor { + id: "https://example.org/test".to_string(), + ..Default::default() + }), + ..Default::default() + }; + let result = clean_profile_create_data(&mut profile_data); + assert_eq!(result.is_ok(), true); + } }