// Package admin gère la configuration et les permissions de l'interface d'administration. package admin import ( "fmt" "os" "strings" "gopkg.in/yaml.v3" ) // AdminConfig représente la configuration complète de l'admin. type AdminConfig struct { Session SessionConfig `yaml:"session"` RateLimit RateLimitConfig `yaml:"rate_limit"` TwoFA TwoFAConfig `yaml:"two_fa"` Users []AdminUser `yaml:"users"` } // TwoFAConfig configure l'authentification à deux facteurs. type TwoFAConfig struct { Enabled bool `yaml:"enabled"` IssuerName string `yaml:"issuer_name"` RequiredRoles []string `yaml:"required_roles"` // rôles obligés d'avoir 2FA } // SessionConfig configure les sessions. type SessionConfig struct { SecretFile string `yaml:"secret_file"` MaxAge int `yaml:"max_age"` // secondes CookieName string `yaml:"cookie_name"` Secret string `yaml:"-"` // chargé depuis fichier } // RateLimitConfig configure le rate limiting. type RateLimitConfig struct { LoginMax int `yaml:"login_max"` LoginWindow int `yaml:"login_window"` // secondes } // AdminUser représente un utilisateur admin. type AdminUser struct { Username string `yaml:"username"` PasswordHash string `yaml:"password_hash"` Role string `yaml:"role"` Email string `yaml:"email"` Apps []string `yaml:"apps,omitempty"` // pour app_admin Permissions []string `yaml:"permissions,omitempty"` // pour app_admin // 2FA TwoFAEnabled bool `yaml:"two_fa_enabled,omitempty"` TwoFASecret string `yaml:"two_fa_secret,omitempty"` // base32 encoded BackupCodes []string `yaml:"backup_codes,omitempty"` // bcrypt hashed } // IsSuperAdmin retourne true si l'utilisateur est super_admin. func (u *AdminUser) IsSuperAdmin() bool { return u.Role == "super_admin" } // NeedsTwoFA retourne true si l'utilisateur doit utiliser 2FA. func (u *AdminUser) NeedsTwoFA(cfg *TwoFAConfig) bool { if !cfg.Enabled { return false } // Si 2FA activé pour cet utilisateur if u.TwoFAEnabled { return true } // Si le rôle est dans la liste des rôles obligés for _, role := range cfg.RequiredRoles { if u.Role == role { return true } } return false } // LoadAdminConfig charge la configuration admin depuis un fichier YAML. func LoadAdminConfig(path string) (*AdminConfig, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("read admin config: %w", err) } var cfg AdminConfig if err := yaml.Unmarshal(data, &cfg); err != nil { return nil, fmt.Errorf("parse admin config: %w", err) } // Valeurs par défaut if cfg.Session.MaxAge == 0 { cfg.Session.MaxAge = 3600 // 1 heure } if cfg.Session.CookieName == "" { cfg.Session.CookieName = "sogoms_admin_sid" } if cfg.RateLimit.LoginMax == 0 { cfg.RateLimit.LoginMax = 5 } if cfg.RateLimit.LoginWindow == 0 { cfg.RateLimit.LoginWindow = 60 } if cfg.TwoFA.IssuerName == "" { cfg.TwoFA.IssuerName = "SOGOMS Admin" } // Charger le secret de session depuis le fichier if cfg.Session.SecretFile != "" { secretData, err := os.ReadFile(cfg.Session.SecretFile) if err != nil { return nil, fmt.Errorf("read session secret: %w", err) } cfg.Session.Secret = strings.TrimSpace(string(secretData)) } // Valider if len(cfg.Users) == 0 { return nil, fmt.Errorf("no users defined") } if cfg.Session.Secret == "" { return nil, fmt.Errorf("session secret is required") } return &cfg, nil } // GetUser retourne un utilisateur par son username. func (cfg *AdminConfig) GetUser(username string) *AdminUser { for i := range cfg.Users { if cfg.Users[i].Username == username { return &cfg.Users[i] } } return nil } // GetUserByEmail retourne un utilisateur par son email. func (cfg *AdminConfig) GetUserByEmail(email string) *AdminUser { for i := range cfg.Users { if cfg.Users[i].Email == email { return &cfg.Users[i] } } return nil } // SaveAdminConfig sauvegarde la configuration admin dans un fichier YAML. func SaveAdminConfig(cfg *AdminConfig, path string) error { // Créer une copie sans le secret en mémoire pour la sauvegarde saveCfg := *cfg saveCfg.Session.Secret = "" // Ne pas sauvegarder le secret déchiffré data, err := yaml.Marshal(&saveCfg) if err != nil { return fmt.Errorf("marshal admin config: %w", err) } if err := os.WriteFile(path, data, 0600); err != nil { return fmt.Errorf("write admin config: %w", err) } return nil }