using System.Security.Claims; using KernelRecordsMVC.Application.ViewModels; using KernelRecordsMVC.Infrastructure.Data; using KernelRecordsMVC.Models; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace KernelRecordsMVC.Web.Controllers; public class AccountController : Controller { private readonly KernelRecordsContext _context; public AccountController(KernelRecordsContext context) { _context = context; } // ========================================================= // REGISTER - GET // ========================================================= [HttpGet] public IActionResult Register() { return View(); } // ========================================================= // REGISTER - POST // ========================================================= [HttpPost] [ValidateAntiForgeryToken] public IActionResult Register(RegisterViewModel model) { if (!ModelState.IsValid) return View(model); if (_context.Users.Any(x => x.Username == model.Username)) { ModelState.AddModelError( "Username", "Username is already taken."); return View(model); } if (_context.Users.Any(x => x.Email == model.Email)) { ModelState.AddModelError( "Email", "Email is already registered."); return View(model); } var user = new User { Email = model.Email, Username = model.Username, // TODO: // Replace with password hashing later. Password = model.Password, DateCreated = DateTime.Today, ShippingAddress = model.ShippingAddress, TelephoneNumber = model.TelephoneNumber }; _context.Users.Add(user); _context.SaveChanges(); // Every normal registered user is a Consumer. var consumer = new Consumer { UserId = user.UserId, PointsCollected = 0 }; _context.Consumers.Add(consumer); _context.SaveChanges(); return RedirectToAction(nameof(Login)); } // ========================================================= // LOGIN - GET // ========================================================= [HttpGet] public IActionResult Login() { return View(); } // ========================================================= // LOGIN - POST // ========================================================= [HttpPost] [ValidateAntiForgeryToken] public async Task Login( LoginViewModel model) { if (!ModelState.IsValid) return View(model); var user = _context.Users .FirstOrDefault(x => x.Username == model.Username && x.Password == model.Password); if (user == null) { ModelState.AddModelError( "", "Invalid username or password."); return View(model); } // ===================================================== // DETERMINE ACCOUNT TYPE // ===================================================== var admin = _context.Admins .FirstOrDefault(x => x.UserId == user.UserId); string role; if (admin != null) { role = "Admin"; } else { role = "Consumer"; } // ===================================================== // CREATE CLAIMS // ===================================================== var claims = new List { new Claim( ClaimTypes.NameIdentifier, user.UserId.ToString()), new Claim( ClaimTypes.Name, user.Username), new Claim( ClaimTypes.Email, user.Email), new Claim( ClaimTypes.Role, role) }; if (admin != null) { claims.Add( new Claim( "AdminType", admin.Type.ToString())); } var identity = new ClaimsIdentity( claims, "Cookies"); var principal = new ClaimsPrincipal(identity); // ===================================================== // SIGN IN // ===================================================== await HttpContext.SignInAsync( "Cookies", principal); // ===================================================== // KEEP SESSION FOR EXISTING CODE // ===================================================== HttpContext.Session.SetInt32( "UserId", checked((int)user.UserId)); HttpContext.Session.SetString( "Username", user.Username); HttpContext.Session.SetString( "Role", role); if (admin != null) { HttpContext.Session.SetString( "AdminType", admin.Type.ToString()); } return RedirectToAction( "Index", "Home"); } // ========================================================= // PROFILE - GET // ========================================================= [HttpGet] public IActionResult Profile() { // Get logged-in user's ID from authentication claims. var userIdClaim = User.FindFirst( ClaimTypes.NameIdentifier); if (userIdClaim == null) return RedirectToAction(nameof(Login)); if (!long.TryParse( userIdClaim.Value, out var userId)) { return RedirectToAction(nameof(Login)); } // Load user + consumer information. var user = _context.Users .Include(x => x.Consumer) .FirstOrDefault(x => x.UserId == userId); if (user == null) return NotFound(); var model = new ProfileViewModel { UserId = user.UserId, Username = user.Username, Email = user.Email, TelephoneNumber = user.TelephoneNumber, ShippingAddress = user.ShippingAddress, DateCreated = user.DateCreated, PointsCollected = user.Consumer?.PointsCollected ?? 0 }; return View(model); } // ========================================================= // PROFILE - POST // ========================================================= [HttpPost] [ValidateAntiForgeryToken] public async Task Profile( ProfileViewModel model) { // Get the currently authenticated user. var userIdClaim = User.FindFirst( ClaimTypes.NameIdentifier); if (userIdClaim == null) return RedirectToAction(nameof(Login)); if (!long.TryParse( userIdClaim.Value, out var userId)) { return RedirectToAction(nameof(Login)); } if (!ModelState.IsValid) return View(model); var user = _context.Users .FirstOrDefault(x => x.UserId == userId); if (user == null) return NotFound(); // ===================================================== // CHECK EMAIL // ===================================================== var emailExists = _context.Users.Any(x => x.Email == model.Email && x.UserId != userId); if (emailExists) { ModelState.AddModelError( nameof(model.Email), "This email is already registered."); return View(model); } // ===================================================== // UPDATE USER // ===================================================== user.Email = model.Email; user.TelephoneNumber = model.TelephoneNumber; user.ShippingAddress = model.ShippingAddress; _context.SaveChanges(); TempData["Success"] = "Your profile has been updated successfully."; // ===================================================== // REFRESH EMAIL CLAIM // ===================================================== var claims = new List { new Claim( ClaimTypes.NameIdentifier, user.UserId.ToString()), new Claim( ClaimTypes.Name, user.Username), new Claim( ClaimTypes.Email, user.Email), new Claim( ClaimTypes.Role, User.IsInRole("Admin") ? "Admin" : "Consumer") }; var identity = new ClaimsIdentity( claims, "Cookies"); var principal = new ClaimsPrincipal(identity); await HttpContext.SignInAsync( "Cookies", principal); // Keep session synchronized. HttpContext.Session.SetString( "Username", user.Username); return RedirectToAction(nameof(Profile)); } // ========================================================= // LOGOUT // ========================================================= [HttpPost] [ValidateAntiForgeryToken] public async Task Logout() { await HttpContext.SignOutAsync("Cookies"); HttpContext.Session.Clear(); return RedirectToAction( "Index", "Home"); } }