Increase maximum length of display_name to 200 chars and validate it on profile import
This commit is contained in:
parent
690edddbc1
commit
1936219b3d
9 changed files with 91 additions and 13 deletions
|
@ -0,0 +1 @@
|
|||
ALTER TABLE actor_profile ALTER COLUMN display_name TYPE VARCHAR(200);
|
|
@ -1,7 +1,7 @@
|
|||
CREATE TABLE actor_profile (
|
||||
id UUID PRIMARY KEY,
|
||||
username VARCHAR(100) NOT NULL,
|
||||
display_name VARCHAR(100),
|
||||
display_name VARCHAR(200),
|
||||
acct VARCHAR(200) UNIQUE NOT NULL,
|
||||
bio TEXT,
|
||||
bio_source TEXT,
|
||||
|
|
|
@ -135,10 +135,25 @@ pub enum ImportError {
|
|||
#[error(transparent)]
|
||||
FetchError(#[from] FetchError),
|
||||
|
||||
#[error(transparent)]
|
||||
ValidationError(#[from] ValidationError),
|
||||
|
||||
#[error(transparent)]
|
||||
DatabaseError(#[from] DatabaseError),
|
||||
}
|
||||
|
||||
impl From<ImportError> for HttpError {
|
||||
fn from(error: ImportError) -> Self {
|
||||
match error {
|
||||
ImportError::FetchError(error) => {
|
||||
HttpError::ValidationError(error.to_string())
|
||||
},
|
||||
ImportError::ValidationError(error) => error.into(),
|
||||
ImportError::DatabaseError(error) => error.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_or_fetch_profile_by_actor_id(
|
||||
db_client: &impl GenericClient,
|
||||
instance: &Instance,
|
||||
|
@ -187,6 +202,7 @@ pub async fn import_profile_by_actor_address(
|
|||
};
|
||||
};
|
||||
log::info!("fetched profile {}", profile_data.acct);
|
||||
profile_data.clean()?;
|
||||
let profile = create_profile(db_client, &profile_data).await?;
|
||||
Ok(profile)
|
||||
}
|
||||
|
@ -328,14 +344,14 @@ pub async fn process_note(
|
|||
&actor_address,
|
||||
).await {
|
||||
Ok(profile) => profile,
|
||||
Err(ImportError::DatabaseError(error)) => {
|
||||
return Err(error.into());
|
||||
},
|
||||
Err(ImportError::FetchError(error)) => {
|
||||
// Ignore mention if fetcher fails
|
||||
log::warn!("{}", error);
|
||||
continue;
|
||||
},
|
||||
Err(other_error) => {
|
||||
return Err(other_error.into());
|
||||
},
|
||||
}
|
||||
},
|
||||
Err(other_error) => return Err(other_error.into()),
|
||||
|
|
|
@ -58,7 +58,7 @@ async fn create_status(
|
|||
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||
let instance = config.instance();
|
||||
let mut post_data = PostCreateData::from(data.into_inner());
|
||||
post_data.validate()?;
|
||||
post_data.clean()?;
|
||||
// Mentions
|
||||
let mention_map = find_mentioned_profiles(
|
||||
db_client,
|
||||
|
|
|
@ -205,7 +205,7 @@ pub struct PostCreateData {
|
|||
|
||||
impl PostCreateData {
|
||||
/// Validate and clean post data.
|
||||
pub fn validate(&mut self) -> Result<(), ValidationError> {
|
||||
pub fn clean(&mut self) -> Result<(), ValidationError> {
|
||||
let content_safe = clean_html(&self.content);
|
||||
let content_trimmed = content_safe.trim();
|
||||
if content_trimmed.is_empty() {
|
||||
|
@ -233,7 +233,7 @@ mod tests {
|
|||
object_id: None,
|
||||
created_at: None,
|
||||
};
|
||||
assert_eq!(post_data_1.validate().is_ok(), false);
|
||||
assert_eq!(post_data_1.clean().is_ok(), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -249,7 +249,7 @@ mod tests {
|
|||
object_id: None,
|
||||
created_at: None,
|
||||
};
|
||||
assert_eq!(post_data_2.validate().is_ok(), true);
|
||||
assert_eq!(post_data_2.clean().is_ok(), true);
|
||||
assert_eq!(post_data_2.content.as_str(), "test");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
pub mod queries;
|
||||
pub mod types;
|
||||
pub mod validators;
|
||||
|
|
|
@ -11,6 +11,10 @@ use uuid::Uuid;
|
|||
use crate::activitypub::views::get_actor_url;
|
||||
use crate::errors::{ConversionError, ValidationError};
|
||||
use crate::utils::html::clean_html;
|
||||
use super::validators::{
|
||||
validate_username,
|
||||
validate_display_name,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct ExtraField {
|
||||
|
@ -137,6 +141,14 @@ pub struct ProfileCreateData {
|
|||
pub actor: Option<Value>,
|
||||
}
|
||||
|
||||
impl ProfileCreateData {
|
||||
pub fn clean(&self) -> Result<(), ValidationError> {
|
||||
validate_username(&self.username)?;
|
||||
validate_display_name(self.display_name.as_ref())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ProfileUpdateData {
|
||||
pub display_name: Option<String>,
|
||||
pub bio: Option<String>,
|
||||
|
@ -169,7 +181,7 @@ impl ProfileUpdateData {
|
|||
unique_labels.dedup();
|
||||
if unique_labels.len() < self.extra_fields.len() {
|
||||
return Err(ValidationError("duplicate labels"));
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
46
src/models/profiles/validators.rs
Normal file
46
src/models/profiles/validators.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
use crate::errors::ValidationError;
|
||||
|
||||
pub fn validate_username(username: &str) -> Result<(), ValidationError> {
|
||||
if username.len() > 100 {
|
||||
return Err(ValidationError("username is too long"));
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn validate_display_name(display_name: Option<&String>)
|
||||
-> Result<(), ValidationError>
|
||||
{
|
||||
if let Some(display_name) = display_name {
|
||||
if display_name.len() > 200 {
|
||||
return Err(ValidationError("display name is too long"));
|
||||
};
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_validate_username() {
|
||||
let result_1 = validate_username("test");
|
||||
assert!(result_1.is_ok());
|
||||
let result_2 = validate_username(&"x".repeat(101));
|
||||
assert!(result_2.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_display_name() {
|
||||
let display_name = "test".to_string();
|
||||
let result_1 = validate_display_name(Some(&display_name));
|
||||
assert!(result_1.is_ok());
|
||||
|
||||
let result_2 = validate_display_name(None);
|
||||
assert!(result_2.is_ok());
|
||||
|
||||
let display_name = "x".repeat(201);
|
||||
let result_3 = validate_display_name(Some(&display_name));
|
||||
assert!(result_3.is_err());
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ use uuid::Uuid;
|
|||
|
||||
use crate::errors::ValidationError;
|
||||
use crate::models::profiles::types::DbActorProfile;
|
||||
use crate::models::profiles::validators::validate_username;
|
||||
|
||||
#[derive(FromSql)]
|
||||
#[postgres(name = "user_account")]
|
||||
|
@ -53,7 +54,7 @@ pub struct UserCreateData {
|
|||
pub invite_code: Option<String>,
|
||||
}
|
||||
|
||||
fn validate_username(username: &str) -> Result<(), ValidationError> {
|
||||
fn validate_local_username(username: &str) -> Result<(), ValidationError> {
|
||||
// The username regexp should not allow domain names and IP addresses
|
||||
let username_regexp = Regex::new(r"^[a-z0-9_]+$").unwrap();
|
||||
if !username_regexp.is_match(username) {
|
||||
|
@ -66,6 +67,7 @@ impl UserCreateData {
|
|||
/// Validate and clean.
|
||||
pub fn clean(&self) -> Result<(), ValidationError> {
|
||||
validate_username(&self.username)?;
|
||||
validate_local_username(&self.username)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -75,10 +77,10 @@ mod tests {
|
|||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_validate_username() {
|
||||
let result_1 = validate_username("name_1");
|
||||
fn test_validate_local_username() {
|
||||
let result_1 = validate_local_username("name_1");
|
||||
assert_eq!(result_1.is_ok(), true);
|
||||
let result_2 = validate_username("name&");
|
||||
let result_2 = validate_local_username("name&");
|
||||
assert_eq!(result_2.is_ok(), false);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue