@ -4,6 +4,7 @@
//! to start with invalid configuration rather than failing at runtime.
use std ::env ;
use std ::sync ::Arc ;
/// Typed application configuration.
#[ derive(Debug, Clone) ]
@ -12,8 +13,7 @@ pub struct AppConfig {
pub database_url : String ,
// Security
pub session_secret : String ,
pub master_encryption_key : String ,
pub master_encryption_key : Arc < String > ,
// Application
pub app_url : String ,
@ -36,7 +36,6 @@ impl AppConfig {
/// Fails fast with a clear error message if a required variable is missing.
pub fn from_env ( ) -> Result < Self , String > {
let database_url = required_var ( "DATABASE_URL" ) ? ;
let session_secret = required_var ( "SESSION_SECRET" ) ? ;
let master_encryption_key = required_var ( "MASTER_ENCRYPTION_KEY" ) ? ;
let app_url = required_var ( "APP_URL" ) ? ;
let resend_api_key = required_var ( "RESEND_API_KEY" ) ? ;
@ -54,8 +53,7 @@ impl AppConfig {
Ok ( Self {
database_url ,
session_secret ,
master_encryption_key ,
master_encryption_key : Arc ::new ( master_encryption_key ) ,
app_url ,
port ,
static_dir ,
@ -79,10 +77,6 @@ impl AppConfig {
) ;
}
if self . session_secret . len ( ) < 64 {
return Err ( "SESSION_SECRET must be at least 64 characters" . into ( ) ) ;
}
if ! self . app_url . starts_with ( "http://" ) & & ! self . app_url . starts_with ( "https://" ) {
return Err ( "APP_URL must start with http:// or https://" . into ( ) ) ;
}
@ -113,8 +107,7 @@ mod tests {
fn test_validate_valid_config ( ) {
let config = AppConfig {
database_url : "postgres://user:pass@localhost/db" . into ( ) ,
session_secret : "a" . repeat ( 64 ) ,
master_encryption_key : "ab" . repeat ( 32 ) ,
master_encryption_key : Arc ::new ( "ab" . repeat ( 32 ) ) ,
app_url : "https://synth.example.com" . into ( ) ,
port : 8080 ,
static_dir : "./static" . into ( ) ,
@ -130,8 +123,7 @@ mod tests {
fn test_validate_bad_encryption_key_length ( ) {
let config = AppConfig {
database_url : "postgres://user:pass@localhost/db" . into ( ) ,
session_secret : "a" . repeat ( 64 ) ,
master_encryption_key : "abcd" . into ( ) , // too short
master_encryption_key : Arc ::new ( "abcd" . into ( ) ) , // too short
app_url : "https://synth.example.com" . into ( ) ,
port : 8080 ,
static_dir : "./static" . into ( ) ,
@ -148,8 +140,7 @@ mod tests {
fn test_validate_bad_encryption_key_not_hex ( ) {
let config = AppConfig {
database_url : "postgres://user:pass@localhost/db" . into ( ) ,
session_secret : "a" . repeat ( 64 ) ,
master_encryption_key : "zz" . repeat ( 32 ) , // not hex
master_encryption_key : Arc ::new ( "zz" . repeat ( 32 ) ) , // not hex
app_url : "https://synth.example.com" . into ( ) ,
port : 8080 ,
static_dir : "./static" . into ( ) ,
@ -162,30 +153,11 @@ mod tests {
assert! ( err . contains ( "MASTER_ENCRYPTION_KEY" ) ) ;
}
#[ test ]
fn test_validate_short_session_secret ( ) {
let config = AppConfig {
database_url : "postgres://user:pass@localhost/db" . into ( ) ,
session_secret : "short" . into ( ) ,
master_encryption_key : "ab" . repeat ( 32 ) ,
app_url : "https://synth.example.com" . into ( ) ,
port : 8080 ,
static_dir : "./static" . into ( ) ,
resend_api_key : "re_test" . into ( ) ,
email_from : "test@example.com" . into ( ) ,
turnstile_secret_key : "0x4AAA" . into ( ) ,
turnstile_site_key : "0x4BBB" . into ( ) ,
} ;
let err = config . validate ( ) . unwrap_err ( ) ;
assert! ( err . contains ( "SESSION_SECRET" ) ) ;
}
#[ test ]
fn test_validate_bad_app_url_scheme ( ) {
let config = AppConfig {
database_url : "postgres://user:pass@localhost/db" . into ( ) ,
session_secret : "a" . repeat ( 64 ) ,
master_encryption_key : "ab" . repeat ( 32 ) ,
master_encryption_key : Arc ::new ( "ab" . repeat ( 32 ) ) ,
app_url : "ftp://synth.example.com" . into ( ) ,
port : 8080 ,
static_dir : "./static" . into ( ) ,
@ -202,8 +174,7 @@ mod tests {
fn test_validate_trailing_slash ( ) {
let config = AppConfig {
database_url : "postgres://user:pass@localhost/db" . into ( ) ,
session_secret : "a" . repeat ( 64 ) ,
master_encryption_key : "ab" . repeat ( 32 ) ,
master_encryption_key : Arc ::new ( "ab" . repeat ( 32 ) ) ,
app_url : "https://synth.example.com/" . into ( ) ,
port : 8080 ,
static_dir : "./static" . into ( ) ,
@ -220,8 +191,7 @@ mod tests {
fn test_is_secure ( ) {
let config = AppConfig {
database_url : "postgres://user:pass@localhost/db" . into ( ) ,
session_secret : "a" . repeat ( 64 ) ,
master_encryption_key : "ab" . repeat ( 32 ) ,
master_encryption_key : Arc ::new ( "ab" . repeat ( 32 ) ) ,
app_url : "https://synth.example.com" . into ( ) ,
port : 8080 ,
static_dir : "./static" . into ( ) ,