using MaBibli.Shared.Dtos;
namespace MaBibli.Api.Services.Identite;
public interface IFournisseurUtilisateur
{
/// Identité de la requête en cours, jamais null (au pire ).
UtilisateurCourant Obtenir();
}
///
/// Options de l'identité, section Identite de la configuration.
///
public sealed class OptionsIdentite
{
public const string Section = "Identite";
///
/// Utilisateur simulé en développement, où SSOwat n'existe pas.
///
///
/// CLAUDE.md : « prévoir un utilisateur simulé plutôt que de désactiver l'auth ».
/// Il n'est utilisé que si les en-têtes SSOwat sont absents, et n'est renseigné que dans
/// appsettings.Development.json — en production, aucune valeur, donc aucun repli.
///
public string? UtilisateurSimule { get; set; }
public string? EmailSimule { get; set; }
public string? NomCompletSimule { get; set; }
}
///
/// Lit l'identité dans les en-têtes HTTP injectés par SSOwat.
///
///
/// nginx authentifie le visiteur via le portail YunoHost puis pousse YNH_USER,
/// YNH_USER_EMAIL et YNH_USER_FULLNAME. L'application se contente de les lire.
///
/// ⚠️ Ces en-têtes ne sont dignes de confiance que parce que le service n'écoute que sur
/// 127.0.0.1 derrière nginx : exposé directement, n'importe qui pourrait forger
/// YNH_USER. Cette contrainte se règle dans systemd.service / nginx.conf,
/// qui vivent dans le dépôt mabibli_ynh (phase 7) — pas ici.
///
///
public sealed class FournisseurUtilisateurSsowat(
IHttpContextAccessor accesseur,
Microsoft.Extensions.Options.IOptions options) : IFournisseurUtilisateur
{
public const string EnteteUtilisateur = "YNH_USER";
public const string EnteteEmail = "YNH_USER_EMAIL";
public const string EnteteNomComplet = "YNH_USER_FULLNAME";
public UtilisateurCourant Obtenir()
{
var entetes = accesseur.HttpContext?.Request.Headers;
var identifiant = Valeur(entetes, EnteteUtilisateur);
if (identifiant is not null)
{
return new UtilisateurCourant
{
Identifiant = identifiant,
Email = Valeur(entetes, EnteteEmail),
NomComplet = Valeur(entetes, EnteteNomComplet),
Simule = false,
};
}
var simule = options.Value;
if (!string.IsNullOrWhiteSpace(simule.UtilisateurSimule))
{
return new UtilisateurCourant
{
Identifiant = simule.UtilisateurSimule,
Email = simule.EmailSimule,
NomComplet = simule.NomCompletSimule,
Simule = true,
};
}
// Ni en-tête, ni utilisateur simulé : on n'invente pas d'identité.
// Le livre sera enregistré sans traçabilité plutôt que sous un faux nom.
return UtilisateurCourant.Anonyme;
}
private static string? Valeur(IHeaderDictionary? entetes, string nom)
{
if (entetes is null || !entetes.TryGetValue(nom, out var valeurs))
{
return null;
}
var valeur = valeurs.ToString().Trim();
return valeur.Length == 0 ? null : valeur;
}
}