use std::collections::BTreeMap; use std::sync::Arc; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PasswordPolicy { pub min_length: usize, #[serde(default = "default_true")] pub require_upper: bool, #[serde(default = "default_true")] pub require_lower: bool, #[serde(default = "default_true")] pub require_digit: bool, #[serde(default)] pub require_symbol: bool, } fn default_true() -> bool { true } impl Default for PasswordPolicy { fn default() -> Self { Self { min_length: 8, require_upper: true, require_lower: true, require_digit: true, require_symbol: false, } } } #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct OAuthProviderConfig { #[serde(default)] pub enabled: bool, #[serde(default)] pub client_id: String, #[serde(default)] pub client_secret: String, #[serde(default)] pub redirect_uri: String, #[serde(default)] pub scopes: Vec, #[serde(default)] pub authorize_url: String, #[serde(default)] pub token_url: String, #[serde(default)] pub userinfo_url: String, #[serde(default)] pub emails_url: Option, #[serde(default)] pub extra: BTreeMap, } #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct OAuthConfig { #[serde(default)] pub google: Option, #[serde(default)] pub github: Option, #[serde(default)] pub apple: Option, } #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct AgreementDoc { pub key: String, pub version: String, pub updated_at: String, pub title: String, pub content_md: String, } impl AgreementDoc { pub fn placeholder(key: &str) -> Self { Self { key: key.to_string(), version: "0.1.0".to_string(), updated_at: "1970-01-01T00:00:00Z".to_string(), title: format!("{} (placeholder)", key), content_md: format!( "# {}\n\nThis document is a placeholder. Please replace before production.", key ), } } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AgreementsConfig { pub terms: AgreementDoc, pub privacy: AgreementDoc, } impl Default for AgreementsConfig { fn default() -> Self { Self { terms: AgreementDoc::placeholder("terms"), privacy: AgreementDoc::placeholder("privacy"), } } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AuthConfig { #[serde(default)] pub password_policy: PasswordPolicy, #[serde(default = "default_verify_code_ttl")] pub verify_code_ttl_secs: u64, #[serde(default = "default_verify_code_len")] pub verify_code_len: usize, #[serde(default = "default_reset_token_ttl")] pub reset_token_ttl_secs: u64, #[serde(default = "default_reset_token_bytes")] pub reset_token_bytes: usize, #[serde(default = "default_cookie_prefix")] pub cookie_name_prefix: String, #[serde(default = "default_true_bool")] pub cookie_secure: bool, #[serde(default = "default_same_site")] pub cookie_same_site: String, #[serde(default)] pub cookie_domain: Option, #[serde(default = "default_oauth_state_ttl")] pub oauth_state_ttl_secs: u64, #[serde(default = "default_email_rate_limit")] pub email_code_rate_limit_secs: u64, #[serde(default = "default_max_verify_attempts")] pub max_verify_attempts: u32, #[serde(default)] pub auto_register_agreements: Vec, #[serde(default)] pub oauth: OAuthConfig, #[serde(default)] pub agreements: AgreementsConfig, } fn default_verify_code_ttl() -> u64 { 600 } fn default_verify_code_len() -> usize { 6 } fn default_reset_token_ttl() -> u64 { 1800 } fn default_reset_token_bytes() -> usize { 32 } fn default_cookie_prefix() -> String { "auth_".to_string() } fn default_true_bool() -> bool { true } fn default_same_site() -> String { "Strict".to_string() } fn default_oauth_state_ttl() -> u64 { 300 } fn default_email_rate_limit() -> u64 { 60 } fn default_max_verify_attempts() -> u32 { 5 } impl Default for AuthConfig { fn default() -> Self { Self { password_policy: PasswordPolicy::default(), verify_code_ttl_secs: default_verify_code_ttl(), verify_code_len: default_verify_code_len(), reset_token_ttl_secs: default_reset_token_ttl(), reset_token_bytes: default_reset_token_bytes(), cookie_name_prefix: default_cookie_prefix(), cookie_secure: default_true_bool(), cookie_same_site: default_same_site(), cookie_domain: None, oauth_state_ttl_secs: default_oauth_state_ttl(), email_code_rate_limit_secs: default_email_rate_limit(), max_verify_attempts: default_max_verify_attempts(), auto_register_agreements: Vec::new(), oauth: OAuthConfig::default(), agreements: AgreementsConfig::default(), } } } #[allow(dead_code)] pub type SharedAuthConfig = Arc;