middleware_authconfig_fallback.rs
115 lignes · 4449 octets
//! RFC 0022 + RFC 0031 — anti-régression : `AuthService` / //! `AdminService` / `admin_helper` doivent retomber sur //! `AuthConfig::from_env()` quand le consommateur n'injecte pas //! `Extension(Arc<AuthConfig>)` *avant* l'auth middleware dans son //! `ServiceBuilder`. Le commit `d4d6828` avait introduit un `500` //! silencieux dans la branche **protected** (corrigé par RFC 0022) ; //! la branche **optional auth** restait sans fallback jusqu'à la //! RFC 0031, ce qui produisait des `claims = None` silencieux sur //! toutes les routes hors `protected_prefixes`. //! //! Ce fichier vérifie le pré-requis partagé par les deux branches : //! `AuthConfig::from_env()` tourne avec un set minimal de variables //! d'environnement (comme en prod), et la config rechargée reste //! fonctionnellement cohérente (sign → validate roundtrip). Si l'une //! de ces assertions casse, le fallback de `AuthService::call` est //! inutile dans les deux branches. use std::sync::Mutex; use rustwarden_core::auth::jwt::JwtService; use rustwarden_core::config::auth::AuthConfig; use uuid::Uuid; static ENV_LOCK: Mutex<()> = Mutex::new(()); fn set_minimal_env() { // SAFETY: tests under #[tokio::test] run on the same process; the // mutex above serialises mutations. unsafe { std::env::set_var( "JWT_SECRET", "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", ); // Permit non-HTTPS cookies in dev so `from_env` validates without // requiring a TLS endpoint configured. std::env::set_var("APP_DEBUG", "true"); } } fn clear_env() { // SAFETY: see set_minimal_env. unsafe { std::env::remove_var("JWT_SECRET"); std::env::remove_var("APP_DEBUG"); } } #[test] fn auth_config_from_env_succeeds_with_minimal_vars() { let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner()); set_minimal_env(); let cfg = AuthConfig::from_env(); assert!( cfg.is_ok(), "AuthConfig::from_env must succeed with JWT_SECRET set: got {cfg:?}", ); } #[test] fn auth_config_from_env_fails_without_jwt_secret() { let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner()); clear_env(); // Without JWT_SECRET, from_env must surface an error rather than // silently using a default — the fallback in AuthService surfaces // this as a `tracing::error!` line and a 500. let cfg = AuthConfig::from_env(); assert!( cfg.is_err(), "AuthConfig::from_env must reject missing JWT_SECRET: got {cfg:?}", ); } #[test] fn auth_config_from_env_is_idempotent() { let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner()); set_minimal_env(); let a = AuthConfig::from_env().expect("first load"); let b = AuthConfig::from_env().expect("second load"); // Two consecutive calls must yield equal jwt_expiration_minutes — // the trait-less smoke check that the env-driven loader is stable. assert_eq!( a.jwt_expiration_minutes, b.jwt_expiration_minutes, "AuthConfig::from_env must be deterministic across calls", ); } #[test] fn auth_config_from_env_yields_signature_consistent_loader() { // RFC 0031 — la branche optional auth de `AuthService` signe et // valide les JWT avec un `AuthConfig::from_env()` rechargé. Cette // assertion garantit que deux chargements successifs produisent des // clefs HMAC fonctionnellement équivalentes : un token émis avec // `from_env()` doit pouvoir être validé par un autre `from_env()` // tournant dans le même process. Sans cette propriété, le fallback // de RFC 0031 émettrait silencieusement `claims = None`. let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner()); set_minimal_env(); let signer = AuthConfig::from_env().expect("signer load"); let validator = AuthConfig::from_env().expect("validator load"); let user_id = Uuid::new_v4(); let token = JwtService::generate_token(&signer, user_id, "fallback_user", Vec::new(), Vec::new()) .expect("token signing must succeed"); let claims = JwtService::validate_token(&validator, &token) .expect("token signed by from_env() must validate against another from_env()"); assert_eq!( claims.sub, user_id, "validated claims must round-trip the user id" ); assert_eq!(claims.username, "fallback_user"); }
GitRust