diff --git a/src/backend/Cargo.toml b/src/backend/Cargo.toml index 2e73646..fca1596 100644 --- a/src/backend/Cargo.toml +++ b/src/backend/Cargo.toml @@ -7,8 +7,10 @@ edition = "2024" anyhow = "1.0.100" argon2 = "0.5.3" axum = "0.8.7" +lazy_static = "1.5.0" password-hash = "0.5.0" rand_core = { version = "0.6", features = ["getrandom"] } +regex = "1.12.2" serde = { version = "1.0.228", features = ["derive", "serde_derive"] } serde_json = "1.0.145" tokio = { version = "1.48.0", features = ["macros", "rt-multi-thread"] } diff --git a/src/backend/src/domain/mod.rs b/src/backend/src/domain/mod.rs index 22d12a3..634c158 100644 --- a/src/backend/src/domain/mod.rs +++ b/src/backend/src/domain/mod.rs @@ -1 +1,2 @@ pub mod user; +pub mod validation; diff --git a/src/backend/src/domain/user.rs b/src/backend/src/domain/user.rs index cd6c53a..bdb9cc4 100644 --- a/src/backend/src/domain/user.rs +++ b/src/backend/src/domain/user.rs @@ -1,16 +1,29 @@ use std::fmt::Display; use axum::response::IntoResponse; +use lazy_static::lazy_static; +use regex::Regex; use serde::{Deserialize, Serialize}; +use validator::Validate; + +use crate::domain::validation; use crate::auth; -#[derive(Debug, Clone, Deserialize)] +#[derive(Debug, Clone, Deserialize, Validate)] pub struct NewUser { + #[validate( + length(min = 4, max = 16), + custom(function = "validation::validate_alphanum") + )] username: String, + #[validate(email)] email: Option, + #[validate(length(min = 8))] password: String, + #[validate(length(min = 1, max = 64))] first_name: Option, + #[validate(length(min = 1, max = 64))] last_name: Option, } diff --git a/src/backend/src/domain/validation.rs b/src/backend/src/domain/validation.rs new file mode 100644 index 0000000..16187ec --- /dev/null +++ b/src/backend/src/domain/validation.rs @@ -0,0 +1,15 @@ +use lazy_static::lazy_static; +use regex::Regex; +use validator::ValidationError; + +lazy_static! { + static ref ALPHANUM: Regex = Regex::new(r"^[a-zA-Z0-9]+$").unwrap(); +} + +pub fn validate_alphanum(input: &str) -> Result<(), ValidationError> { + if ALPHANUM.is_match(input) { + Ok(()) + } else { + Err(ValidationError::new("alphanum")) + } +} diff --git a/src/backend/src/router/user_routes.rs b/src/backend/src/router/user_routes.rs index 507eee1..76e1c05 100644 --- a/src/backend/src/router/user_routes.rs +++ b/src/backend/src/router/user_routes.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use axum::{Json, extract::State, http::StatusCode}; use serde_json::json; use tracing::{debug, error, info, warn}; +use validator::Validate; use crate::{ domain::user::{InternalUser, NewUser, User}, @@ -13,6 +14,10 @@ pub async fn create( State(state): State>, Json(new_user): Json, ) -> Result, StatusCode> { + if let Err(_) = new_user.validate() { + return Err(StatusCode::BAD_REQUEST); + } + let internal = InternalUser::try_from(new_user).map_err(|e| { error!("Conversion to InternalUser failed: {e}"); StatusCode::INTERNAL_SERVER_ERROR