Files
administration/Controllers/AccountController.cs
2025-09-03 20:17:50 +02:00

186 lines
6.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<User> _hasher;
public AccountController(LayoutDataContext db, IPasswordHasher<User> 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<IActionResult> 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 dauth
var claims = new List<Claim>
{
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<IActionResult> 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<IActionResult> 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 lutilisateur 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 laffiche
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<IActionResult> 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<IActionResult> 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 dun compte admin si tu nas rien en BDD
[HttpPost, ValidateAntiForgeryToken]
public async Task<IActionResult> 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éé.");
}
}