155 lines
5.0 KiB
C#
155 lines
5.0 KiB
C#
using System.Text.Json;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using PlotLine.Data;
|
|
using PlotLine.Services;
|
|
using PlotLine.ViewModels;
|
|
|
|
namespace PlotLine.Controllers;
|
|
|
|
[Authorize]
|
|
public sealed class ManageAccountController(
|
|
ICurrentUserService currentUser,
|
|
IUserRepository users,
|
|
IAuthService authService,
|
|
ITwoFactorService twoFactorService,
|
|
ISubscriptionService subscriptions) : Controller
|
|
{
|
|
[HttpGet]
|
|
public async Task<IActionResult> Index()
|
|
{
|
|
var user = await GetCurrentUserAsync();
|
|
if (user is null)
|
|
{
|
|
return RedirectToAction("Login", "Account");
|
|
}
|
|
|
|
return View(new ManageAccountViewModel
|
|
{
|
|
User = user,
|
|
Subscription = await subscriptions.GetCurrentSubscriptionAsync(user.UserID)
|
|
});
|
|
}
|
|
|
|
[HttpGet]
|
|
public IActionResult ChangePassword()
|
|
{
|
|
return View(new ChangePasswordViewModel());
|
|
}
|
|
|
|
[HttpPost]
|
|
[ValidateAntiForgeryToken]
|
|
public async Task<IActionResult> ChangePassword(ChangePasswordViewModel model)
|
|
{
|
|
if (!ModelState.IsValid)
|
|
{
|
|
return View(model);
|
|
}
|
|
|
|
var result = await authService.ChangePasswordAsync(model);
|
|
if (!result.Success)
|
|
{
|
|
ModelState.AddModelError(string.Empty, result.ErrorMessage ?? "Your password could not be changed.");
|
|
return View(model);
|
|
}
|
|
|
|
TempData["AuthMessage"] = "Your password has been changed. Please sign in again.";
|
|
return RedirectToAction("Login", "Account");
|
|
}
|
|
|
|
[HttpGet]
|
|
public async Task<IActionResult> EnableTwoFactor()
|
|
{
|
|
var user = await GetCurrentUserAsync();
|
|
if (user is null)
|
|
{
|
|
return RedirectToAction("Login", "Account");
|
|
}
|
|
|
|
var secret = twoFactorService.GenerateSecretKey();
|
|
var uri = twoFactorService.GenerateQrCodeUri(user.Email, secret);
|
|
return View(new EnableTwoFactorViewModel
|
|
{
|
|
SecretKey = secret,
|
|
QrCodeUri = uri,
|
|
QrCodeImageDataUri = twoFactorService.GenerateQrCodeDataUri(uri)
|
|
});
|
|
}
|
|
|
|
[HttpPost]
|
|
[ValidateAntiForgeryToken]
|
|
public async Task<IActionResult> EnableTwoFactor(EnableTwoFactorViewModel model)
|
|
{
|
|
if (!ModelState.IsValid)
|
|
{
|
|
model.QrCodeUri = twoFactorService.GenerateQrCodeUri(currentUser.Email ?? string.Empty, model.SecretKey);
|
|
model.QrCodeImageDataUri = twoFactorService.GenerateQrCodeDataUri(model.QrCodeUri);
|
|
return View(model);
|
|
}
|
|
|
|
var result = await authService.EnableTwoFactorAsync(currentUser.UserId!.Value, model.SecretKey, model.Code);
|
|
if (!result.Success)
|
|
{
|
|
ModelState.AddModelError(string.Empty, result.ErrorMessage ?? "Two-factor authentication could not be enabled.");
|
|
model.QrCodeUri = twoFactorService.GenerateQrCodeUri(currentUser.Email ?? string.Empty, model.SecretKey);
|
|
model.QrCodeImageDataUri = twoFactorService.GenerateQrCodeDataUri(model.QrCodeUri);
|
|
return View(model);
|
|
}
|
|
|
|
TempData["RecoveryCodes"] = JsonSerializer.Serialize(result.RecoveryCodes);
|
|
TempData["AuthMessage"] = "Two-factor authentication has been enabled.";
|
|
return RedirectToAction(nameof(RecoveryCodes));
|
|
}
|
|
|
|
[HttpGet]
|
|
public IActionResult DisableTwoFactor()
|
|
{
|
|
return View(new DisableTwoFactorViewModel());
|
|
}
|
|
|
|
[HttpPost]
|
|
[ValidateAntiForgeryToken]
|
|
public async Task<IActionResult> DisableTwoFactor(DisableTwoFactorViewModel model)
|
|
{
|
|
if (!ModelState.IsValid)
|
|
{
|
|
return View(model);
|
|
}
|
|
|
|
var result = await authService.DisableTwoFactorAsync(currentUser.UserId!.Value, model.CurrentPassword);
|
|
if (!result.Success)
|
|
{
|
|
ModelState.AddModelError(string.Empty, result.ErrorMessage ?? "Two-factor authentication could not be disabled.");
|
|
return View(model);
|
|
}
|
|
|
|
TempData["AuthMessage"] = "Two-factor authentication has been disabled.";
|
|
return RedirectToAction(nameof(Index));
|
|
}
|
|
|
|
[HttpGet]
|
|
public IActionResult RecoveryCodes()
|
|
{
|
|
var codes = TempData["RecoveryCodes"] is string json
|
|
? JsonSerializer.Deserialize<IReadOnlyList<string>>(json) ?? Array.Empty<string>()
|
|
: Array.Empty<string>();
|
|
|
|
return View(new RecoveryCodesViewModel { RecoveryCodes = codes });
|
|
}
|
|
|
|
[HttpPost]
|
|
[ValidateAntiForgeryToken]
|
|
public async Task<IActionResult> RegenerateRecoveryCodes()
|
|
{
|
|
var codes = await authService.GenerateRecoveryCodesAsync(currentUser.UserId!.Value);
|
|
TempData["RecoveryCodes"] = JsonSerializer.Serialize(codes);
|
|
TempData["AuthMessage"] = "New recovery codes have been generated.";
|
|
return RedirectToAction(nameof(RecoveryCodes));
|
|
}
|
|
|
|
private async Task<PlotLine.Models.AppUser?> GetCurrentUserAsync()
|
|
{
|
|
return currentUser.UserId is int userId ? await users.GetByIdAsync(userId) : null;
|
|
}
|
|
}
|