using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using SIFinance.Shared.Entities; namespace SIFinance.Shared; public class CurrentUserService(IHttpContextAccessor httpContextAccessor) { public Task GetOrCreateCurrentUserIdAsync(CancellationToken ct = default) { var principal = httpContextAccessor.HttpContext?.User ?? throw new InvalidOperationException("Aucun contexte HTTP disponible pour résoudre l'utilisateur courant."); var idClaim = principal.FindFirst("id")?.Value ?? throw new UnauthorizedAccessException("Le jeton ne contient pas de claim 'id' — utilisateur non identifiable."); return Task.FromResult(int.Parse(idClaim)); } // Vérifie si l'utilisateur courant possède le rôle donné (ex. "FINANCE"). // Passe par une jointure utilisateurs <-> utilisateur_roles via azure_oid, car // le JWT ne porte que l'Id interne, pas l'azure_oid. public async Task HasRoleAsync(string role, SIFinanceDbContext db, CancellationToken ct = default) { var userId = await GetOrCreateCurrentUserIdAsync(ct); // Les habilitations fonctionnelles viennent exclusivement de // core.utilisateur_roles. Un profil Entra ou le statut Responsable ne donne // jamais à lui seul l'accès aux écrans ou actions Finance. return await db.UtilisateurRoles .AsNoTracking() .Join(db.Utilisateurs.AsNoTracking(), ur => ur.AzureOid, u => u.AzureOid, (ur, u) => new { u.Id, ur.Role }) .AnyAsync(x => x.Id == userId && x.Role == role, ct); } }