Move profile data cleaning code to validators::profiles module
This commit is contained in:
parent
441850dd21
commit
ef852d781e
4 changed files with 73 additions and 65 deletions
|
@ -21,7 +21,10 @@ use crate::models::{
|
||||||
ProfileUpdateData,
|
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
|
pub const ACTOR_IMAGE_MAX_SIZE: usize = 5 * 1000 * 1000; // 5 MB
|
||||||
|
|
||||||
|
@ -173,7 +176,7 @@ pub async fn create_remote_profile(
|
||||||
emojis,
|
emojis,
|
||||||
actor_json: Some(actor),
|
actor_json: Some(actor),
|
||||||
};
|
};
|
||||||
profile_data.clean()?;
|
clean_profile_create_data(&mut profile_data)?;
|
||||||
let profile = create_profile(db_client, profile_data).await?;
|
let profile = create_profile(db_client, profile_data).await?;
|
||||||
Ok(profile)
|
Ok(profile)
|
||||||
}
|
}
|
||||||
|
@ -231,7 +234,7 @@ pub async fn update_remote_profile(
|
||||||
emojis,
|
emojis,
|
||||||
actor_json: Some(actor),
|
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?;
|
let profile = update_profile(db_client, &profile.id, profile_data).await?;
|
||||||
Ok(profile)
|
Ok(profile)
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,6 +99,7 @@ use crate::models::{
|
||||||
},
|
},
|
||||||
users::types::{Role, UserCreateData},
|
users::types::{Role, UserCreateData},
|
||||||
};
|
};
|
||||||
|
use crate::validators::profiles::clean_profile_update_data;
|
||||||
use super::helpers::{follow_or_create_request, get_relationship};
|
use super::helpers::{follow_or_create_request, get_relationship};
|
||||||
use super::types::{
|
use super::types::{
|
||||||
Account,
|
Account,
|
||||||
|
@ -246,7 +247,7 @@ async fn update_credentials(
|
||||||
¤t_user.profile,
|
¤t_user.profile,
|
||||||
&config.media_dir(),
|
&config.media_dir(),
|
||||||
)?;
|
)?;
|
||||||
profile_data.clean()?;
|
clean_profile_update_data(&mut profile_data)?;
|
||||||
current_user.profile = update_profile(
|
current_user.profile = update_profile(
|
||||||
db_client,
|
db_client,
|
||||||
¤t_user.id,
|
¤t_user.id,
|
||||||
|
|
|
@ -20,14 +20,7 @@ use crate::database::{
|
||||||
json_macro::{json_from_sql, json_to_sql},
|
json_macro::{json_from_sql, json_to_sql},
|
||||||
DatabaseTypeError,
|
DatabaseTypeError,
|
||||||
};
|
};
|
||||||
use crate::errors::ValidationError;
|
|
||||||
use crate::models::emojis::types::DbEmoji;
|
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)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub struct ProfileImage {
|
pub struct ProfileImage {
|
||||||
|
@ -456,25 +449,6 @@ pub struct ProfileCreateData {
|
||||||
pub actor_json: Option<Actor>,
|
pub actor_json: Option<Actor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
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 struct ProfileUpdateData {
|
||||||
pub display_name: Option<String>,
|
pub display_name: Option<String>,
|
||||||
pub bio: Option<String>,
|
pub bio: Option<String>,
|
||||||
|
@ -506,20 +480,6 @@ impl ProfileUpdateData {
|
||||||
});
|
});
|
||||||
self.payment_options.push(option);
|
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 {
|
impl From<&DbActorProfile> for ProfileUpdateData {
|
||||||
|
@ -546,7 +506,6 @@ impl From<&DbActorProfile> for ProfileUpdateData {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::activitypub::actors::types::Actor;
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -588,20 +547,4 @@ mod tests {
|
||||||
let serialized = serde_json::to_string(&payment_option).unwrap();
|
let serialized = serde_json::to_string(&payment_option).unwrap();
|
||||||
assert_eq!(serialized, r#"{"payment_type":2,"chain_id":"eip155:1"}"#);
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,11 @@ use regex::Regex;
|
||||||
use mitra_utils::html::{clean_html, clean_html_strict};
|
use mitra_utils::html::{clean_html, clean_html_strict};
|
||||||
|
|
||||||
use crate::errors::ValidationError;
|
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 USERNAME_RE: &str = r"^[a-zA-Z0-9_\.-]+$";
|
||||||
const DISPLAY_NAME_MAX_LENGTH: usize = 200;
|
const DISPLAY_NAME_MAX_LENGTH: usize = 200;
|
||||||
|
@ -26,7 +30,7 @@ pub fn validate_username(username: &str) -> Result<(), ValidationError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate_display_name(display_name: &str)
|
fn validate_display_name(display_name: &str)
|
||||||
-> Result<(), ValidationError>
|
-> Result<(), ValidationError>
|
||||||
{
|
{
|
||||||
if display_name.chars().count() > DISPLAY_NAME_MAX_LENGTH {
|
if display_name.chars().count() > DISPLAY_NAME_MAX_LENGTH {
|
||||||
|
@ -35,7 +39,7 @@ pub fn validate_display_name(display_name: &str)
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clean_bio(bio: &str, is_remote: bool) -> Result<String, ValidationError> {
|
fn clean_bio(bio: &str, is_remote: bool) -> Result<String, ValidationError> {
|
||||||
let cleaned_bio = if is_remote {
|
let cleaned_bio = if is_remote {
|
||||||
// Remote profile
|
// Remote profile
|
||||||
let truncated_bio: String = bio.chars().take(BIO_MAX_LENGTH).collect();
|
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<String, ValidationError>
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validates extra fields and removes fields with empty labels
|
/// Validates extra fields and removes fields with empty labels
|
||||||
pub fn clean_extra_fields(
|
fn clean_extra_fields(
|
||||||
extra_fields: &[ExtraField],
|
extra_fields: &[ExtraField],
|
||||||
is_remote: bool,
|
is_remote: bool,
|
||||||
) -> Result<Vec<ExtraField>, ValidationError> {
|
) -> Result<Vec<ExtraField>, ValidationError> {
|
||||||
|
@ -83,8 +87,49 @@ pub fn clean_extra_fields(
|
||||||
Ok(cleaned_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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::activitypub::actors::types::Actor;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -133,4 +178,20 @@ mod tests {
|
||||||
assert_eq!(result.name, "$ETH");
|
assert_eq!(result.name, "$ETH");
|
||||||
assert_eq!(result.value, "0x1234");
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue