using System.Security.Claims; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using administration.Models.Finances; using administration.Models; using User = administration.Models.User; public class AccountController : Controller { private readonly LayoutDataContext _db; private readonly IPasswordHasher _hasher; public AccountController(LayoutDataContext db, IPasswordHasher hasher) { _db = db; _hasher = hasher; } // GET /Account/Login [HttpGet] public IActionResult Login(string? returnUrl = null) { ViewBag.ReturnUrl = returnUrl; return View(); } // POST /Account/Login [HttpPost, ValidateAntiForgeryToken] public async Task Login(string username, string password, string? returnUrl = null) { if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password)) { ModelState.AddModelError("", "Identifiants requis."); return View(); } var user = await _db.Users.FirstOrDefaultAsync(u => u.Username == username); if (user == null) { ModelState.AddModelError("", "Identifiants invalides."); return View(); } var verify = _hasher.VerifyHashedPassword(user, user.PasswordHash, password); if (verify == PasswordVerificationResult.Failed) { ModelState.AddModelError("", "Identifiants invalides."); return View(); } // Claims pour cookie d’auth var claims = new List { new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), new Claim(ClaimTypes.Name, user.Username) }; var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); var principal = new ClaimsPrincipal(identity); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal); // Compatibilité avec ton code existant HttpContext.Session.SetInt32("UserId", user.Id); if (!string.IsNullOrWhiteSpace(returnUrl) && Url.IsLocalUrl(returnUrl)) return Redirect(returnUrl); return RedirectToAction("Index", "Home"); } // POST /Account/Logout [HttpPost, ValidateAntiForgeryToken] public async Task Logout() { await HttpContext.SignOutAsync(); HttpContext.Session.Clear(); return RedirectToAction("Login"); } // GET /Account/ForgotPassword [HttpGet] public IActionResult ForgotPassword() => View(); // POST /Account/ForgotPassword [HttpPost, ValidateAntiForgeryToken] public async Task ForgotPassword(string usernameOrEmail) { if (string.IsNullOrWhiteSpace(usernameOrEmail)) { ModelState.AddModelError("", "Champ requis."); return View(); } var user = await _db.Users .FirstOrDefaultAsync(u => u.Username == usernameOrEmail); // Par sécurité, on ne révèle pas si l’utilisateur existe if (user != null) { // Génère un token simple (tu peux le hasher si tu veux) var token = Convert.ToBase64String(Guid.NewGuid().ToByteArray()) .Replace("+", "-").Replace("/", "_").TrimEnd('='); user.ResetToken = token; user.ResetTokenExpiresAt = DateTimeOffset.UtcNow.AddHours(1); await _db.SaveChangesAsync(); var resetUrl = Url.Action("ResetPassword", "Account", new { token = token }, Request.Scheme); // TODO: envoyer resetUrl par email. // Temporaire : on l’affiche TempData["ResetLink"] = resetUrl; } TempData["Info"] = "Si un compte existe, un lien de réinitialisation a été envoyé."; return RedirectToAction("ForgotPassword"); } // GET /Account/ResetPassword?token=... [HttpGet] public async Task ResetPassword(string token) { if (string.IsNullOrWhiteSpace(token)) return RedirectToAction("Login"); var user = await _db.Users.FirstOrDefaultAsync(u => u.ResetToken == token && u.ResetTokenExpiresAt > DateTimeOffset.UtcNow); if (user == null) { TempData["Error"] = "Lien invalide ou expiré."; return RedirectToAction("ForgotPassword"); } ViewBag.Token = token; return View(); } // POST /Account/ResetPassword [HttpPost, ValidateAntiForgeryToken] public async Task ResetPassword(string token, string newPassword, string confirmPassword) { if (string.IsNullOrWhiteSpace(token)) { TempData["Error"] = "Token manquant."; return RedirectToAction("ForgotPassword"); } if (string.IsNullOrWhiteSpace(newPassword) || newPassword != confirmPassword) { ModelState.AddModelError("", "Les mots de passe ne correspondent pas."); ViewBag.Token = token; return View(); } var user = await _db.Users.FirstOrDefaultAsync(u => u.ResetToken == token && u.ResetTokenExpiresAt > DateTimeOffset.UtcNow); if (user == null) { TempData["Error"] = "Lien invalide ou expiré."; return RedirectToAction("ForgotPassword"); } user.PasswordHash = _hasher.HashPassword(user, newPassword); user.ResetToken = null; user.ResetTokenExpiresAt = null; await _db.SaveChangesAsync(); TempData["Ok"] = "Mot de passe réinitialisé. Connecte-toi."; return RedirectToAction("Login"); } // (Optionnel) création rapide d’un compte admin si tu n’as rien en BDD [HttpPost, ValidateAntiForgeryToken] public async Task SeedAdmin(string username = "admin", string password = "ChangeMe!123") { if (await _db.Users.AnyAsync()) return BadRequest("Déjà des utilisateurs en BDD."); var u = new User { Username = username }; u.PasswordHash = _hasher.HashPassword(u, password); _db.Users.Add(u); await _db.SaveChangesAsync(); return Ok("Admin créé."); } }