parametres-dynamiques.md 11579 octets

Paramètres dynamiques

Ce document explique comment fonctionne la configuration de la plateforme, les deux mécanismes en jeu, leur précédence, et les pièges à connaître.


Vue d'ensemble : deux systèmes distincts

Gitrust utilise deux systèmes de configuration indépendants :

SystèmeSourceModifiable à chaudVisible dans l'UI admin
Configuration statiqueFichier .env + variables d'environnementNon (restart requis)Non
Configuration dynamiqueTable app_settings (PostgreSQL)Oui (effet immédiat)Oui (/admin/settings)

Il existe un cas hybride pour OAuth (DB prioritaire, .env en fallback).


1. Configuration statique (.env)

Principe

Les variables d'environnement sont chargées une seule fois au démarrage par dotenvy::dotenv() et stockées dans des structs Rust immuables. Elles ne sont jamais relues après le boot.

Fichiers de référence

FichierRôle
.env.exampleTemplate documenté avec toutes les variables
.envConfiguration locale (gitignore)
.env.productionConfiguration de déploiement
.env.testConfiguration de tests

Structs de chargement

StructVariables concernées
GitrustConfigGIT_REPOS_BASE_PATH, SSH_PORT, SSH_LISTEN_ADDR, SSH_HOST_KEY_PATH, CI_*, IMPORT_*
AppConfigAPP_NAME, APP_THEME, APP_DEBUG
AuthConfigJWT_SECRET, JWT_EXPIRATION_MINUTES, SESSION_*, RATE_LIMIT_*, COOKIE_*
EmailConfigSMTP_*, EMAIL_*, IMAP_*

Comportement

  • Chargées dans RustwardenApp::build() au démarrage
  • Stockées dans des Arc<Config> partagés via l'état axum
  • Modifier le .env sans redémarrer n'a aucun effet
  • Ces variables ne sont PAS affichées dans l'interface admin

Liste des variables statiques (non exhaustive)

DATABASE_URL, SERVER_HOST, SERVER_PORT, RUST_LOG,
SSH_HOST_KEY_PATH, SSH_PORT, SSH_LISTEN_ADDR, SSH_PUBLIC_HOST,
JWT_SECRET, JWT_EXPIRATION_MINUTES, JWT_ISSUER,
REFRESH_TOKEN_EXPIRATION_DAYS, REMEMBER_ME_EXPIRATION_DAYS,
SESSION_TIMEOUT_MINUTES, SESSION_BACKEND,
RATE_LIMIT_LOGIN_PER_MINUTE, RATE_LIMIT_REFRESH_PER_MINUTE,
RATE_LIMIT_GENERAL_PER_MINUTE,
APP_DEBUG, COOKIE_SECURE, COOKIE_SAME_SITE,
ADMIN_USERNAME, ADMIN_EMAIL, ADMIN_PASSWORD,
SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD, SMTP_FROM,
EMAIL_BASE_URL, EMAIL_QUEUE_*,
APP_NAME, APP_THEME, DEFAULT_LOCALE,
GIT_REPOS_BASE_PATH,
CI_ENABLED, CI_MAX_CONCURRENT, CI_REMOTE_HOST, CI_WORKSPACE_PATH

2. Configuration dynamique (table app_settings)

Principe

Les réglages dynamiques sont stockés en base PostgreSQL dans la table app_settings et lus à chaque requête via AppSettingsService. Ils sont modifiables à chaud par un administrateur depuis l'interface /admin/settings.

Schéma de la table

CREATE TABLE app_settings (
    id          UUID PRIMARY KEY,
    key         VARCHAR(100) UNIQUE NOT NULL,
    value       TEXT NOT NULL,
    description TEXT,
    updated_by  UUID REFERENCES users(id) ON DELETE SET NULL,
    updated_at  TIMESTAMPTZ NOT NULL
);

Initialisation au démarrage

Au boot, AppSettingsService::initialize_default_settings() crée les réglages par défaut uniquement s'ils n'existent pas encore en base :

let existing = Self::get_setting(db, key).await?;
if existing.is_none() {
    // ... insert valeur par défaut
}

Conséquence critique : une fois qu'un réglage existe en base (créé au premier démarrage ou modifié via l'UI), il n'est jamais écrasé par un redémarrage ultérieur.

Liste des réglages dynamiques et leurs valeurs par défaut

CléDéfautDescription
app_domainenv::var("APP_DOMAIN") ou "localhost"Domaine de l'application
allow_registrationfalseAutoriser l'inscription publique
validation_email_requiredtrueExiger la validation email
audit_log_levelINFONiveau de log d'audit
audit_log_actions["create","update","delete","reset_password"]Actions auditées
password_min_length8Longueur minimum mot de passe
password_require_uppercasefalseExiger des majuscules
password_require_lowercasefalseExiger des minuscules
password_require_digitsfalseExiger des chiffres
password_require_specialfalseExiger des caractères spéciaux
password_change_require_emailtrueConfirmation email pour changement mdp
password_expiration_enabledfalseActiver l'expiration des mots de passe
password_expiration_days0Durée d'expiration (jours)
password_expiration_alert_enabledfalseAlerte email avant expiration
password_expiration_alert_days_before7Jours avant expiration pour alerter
oauth_enabledfalseActiver OAuth/SSO
oauth_google_enabledfalseActiver Google OAuth
oauth_github_enabledfalseActiver GitHub OAuth
oauth_discord_enabledfalseActiver Discord OAuth
oauth_microsoft_enabledfalseActiver Microsoft OAuth
oauth_redirect_base_url""URL de base pour les callbacks OAuth
oauth_auto_registertrueCréer un compte auto au premier login OAuth
oauth_link_existing_accounttrueLier un compte OAuth à un utilisateur existant
oauth_{provider}_client_id""Client ID du provider OAuth
oauth_{provider}_client_secret""Client secret (chiffré AES-256-GCM en base)
oauth_microsoft_tenant""Tenant Azure AD

3. Cas hybride : OAuth

OAuth est le seul sous-système qui combine les deux sources. La logique dans OAuthConfig::load() :

Pour chaque réglage OAuth :
  1. Chercher dans app_settings (DB)
     → si trouvé et non vide : utiliser cette valeur
  2. Sinon : fallback sur la variable d'environnement
     → si absente : utiliser la valeur par défaut codée en dur

Important : OAuthConfig::load() est appelé au démarrage. Le résultat est stocké dans un Arc et n'est pas relu dynamiquement. Donc :

  • Modifier un réglage OAuth via l'UI admin nécessite un redémarrage pour que OAuthConfig soit rechargé
  • Le .env n'est qu'un fallback si la DB n'a pas de valeur

4. Précédence et comportement au redémarrage

Réglages statiques (.env uniquement)

ActionEffet immédiatAprès restart
Modifier .envNonOui

Réglages dynamiques (DB uniquement)

ActionEffet immédiatAprès restart
Modifier via UI adminOuiOui (valeur en DB persiste)
Modifier le .envAucun effetAucun effet

Réglages hybrides (OAuth)

ActionEffet immédiatAprès restart
Modifier via UI adminNon (OAuthConfig est en Arc)Oui (DB prime sur .env)
Modifier .envNonSeulement si aucune valeur en DB

5. Piège principal : les variables .env fantômes

Certaines variables présentes dans .env.example donnent l'illusion de configurer des réglages qui sont en réalité gérés par la base de données :

Variable .envRéglage DB correspondantLa variable .env est-elle lue ?
ALLOW_REGISTRATION=trueallow_registrationNon — valeur par défaut codée en dur ("false")
EMAIL_VALIDATION_REQUIRED=truevalidation_email_requiredNon — valeur par défaut codée en dur ("true")
OAUTH_ENABLED=trueoauth_enabledOui, mais seulement en fallback si la DB n'a pas de valeur

Pour ALLOW_REGISTRATION et EMAIL_VALIDATION_REQUIRED :

  • La variable .env est documentée dans .env.example mais n'est jamais consultée par initialize_default_settings()
  • La valeur initiale est codée en dur dans le service
  • Seule la valeur en base de données (modifiable via /admin/settings) fait foi

Pour app_domain, c'est le seul réglage dynamique qui lit le .env comme seed initial.


6. Diagramme de décision

                    ┌─────────────────────────────┐
                    │  Quel type de réglage ?      │
                    └──────────┬──────────────────-┘
              ┌────────────────┼────────────────┐
              ▼                ▼                ▼
     Infrastructure       Fonctionnel        OAuth
     (port, JWT, SMTP)    (inscription,      (providers,
                          mots de passe)      secrets)
              │                │                │
              ▼                ▼                ▼
         .env seul        DB seule         DB + fallback .env
              │                │                │
              ▼                ▼                ▼
      Restart requis    Effet immédiat    Restart requis
                        via /admin/       (OAuthConfig en Arc)
                        settings

7. Guide pour les administrateurs

Modifier un réglage d'infrastructure

Éditer .env (ou .env.production) puis redémarrer le service :

# Éditer
vim .env

# Redémarrer
systemctl restart gitrust    # production
# ou Ctrl+C + cargo run      # dev

Modifier un réglage fonctionnel

Se connecter en tant qu'administrateur, aller dans /admin/settings, modifier la valeur, et cliquer "Save". L'effet est immédiat, aucun redémarrage nécessaire.

Modifier un réglage OAuth

Via /admin/settings, modifier les valeurs OAuth puis redémarrer le service (la config OAuth est chargée en mémoire au boot et n'est pas relue dynamiquement).

Vérifier la valeur effective d'un réglage

Pour les réglages dynamiques, interroger la base directement :

SELECT key, value, updated_at, updated_by
FROM app_settings
WHERE key = 'allow_registration';

Pour les réglages statiques, vérifier les logs au démarrage (RUST_LOG=debug).


8. Guide pour les développeurs

Ajouter un nouveau réglage dynamique

  1. Ajouter le tuple (clé, valeur_défaut, description) dans AppSettingsService::initialize_default_settings() :

    ("ma_nouvelle_cle", "valeur_par_defaut", Some("Description pour l'UI admin")),
    
  2. Lire la valeur dans le code via le service :

    // Boolean avec défaut
    let enabled = AppSettingsService::get_bool(&db, "ma_nouvelle_cle", false).await?;
    
    // String
    let val = AppSettingsService::get_setting(&db, "ma_nouvelle_cle").await?;
    
  3. La valeur apparaîtra automatiquement dans /admin/settings.

Ajouter un nouveau réglage statique

  1. Ajouter la variable dans .env.example avec documentation complète
  2. Lire la variable dans le struct Config correspondant via env::var()
  3. La valeur ne sera pas visible dans l'interface admin

Ajouter un réglage hybride (DB + fallback .env)

Suivre le pattern OAuth dans crates/rustwarden-core/src/config/oauth.rs :

let ma_valeur = match db_val("ma_cle").await {
    Some(v) => v,
    None => env::var("MA_CLE").unwrap_or_else(|_| "defaut".to_string()),
};