diff --git a/electronicvotingfyp/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml b/electronicvotingfyp/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml index 27651767492b203d41e3f726388e5e6236670c5d..dd9e319fb7c43bfe5cd5dde6fb21df072433f69a 100644 --- a/electronicvotingfyp/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml +++ b/electronicvotingfyp/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml @@ -1 +1 @@ -@using electronicvotingfyp.Areas.Identity.Pages.Account.Manage \ No newline at end of file +@using electronicvotingfyp.Models \ No newline at end of file diff --git a/electronicvotingfyp/Areas/Identity/Pages/Account/_ViewImports.cshtml b/electronicvotingfyp/Areas/Identity/Pages/Account/_ViewImports.cshtml new file mode 100644 index 0000000000000000000000000000000000000000..dd9e319fb7c43bfe5cd5dde6fb21df072433f69a --- /dev/null +++ b/electronicvotingfyp/Areas/Identity/Pages/Account/_ViewImports.cshtml @@ -0,0 +1 @@ +@using electronicvotingfyp.Models \ No newline at end of file diff --git a/electronicvotingfyp/Areas/Identity/Pages/_ViewImports.cshtml b/electronicvotingfyp/Areas/Identity/Pages/_ViewImports.cshtml index c4523e97950544421cc092cf53003073a7902dce..914f21d18b9a8b041ecd5587019baaeff8ff30b4 100644 --- a/electronicvotingfyp/Areas/Identity/Pages/_ViewImports.cshtml +++ b/electronicvotingfyp/Areas/Identity/Pages/_ViewImports.cshtml @@ -1,4 +1,4 @@ @using Microsoft.AspNetCore.Identity @using electronicvotingfyp.Areas.Identity -@using electronicvotingfyp.Areas.Identity.Pages +@*@using electronicvotingfyp.Areas.Identity.Pages*@ @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/electronicvotingfyp/Controllers/ElectionController.cs b/electronicvotingfyp/Controllers/ElectionController.cs index 5ca75b91b4a7e00a4fa100c8f47ec031ac095c90..71728d1316ab665a59dbcc53c2ab7452e7d234a5 100644 --- a/electronicvotingfyp/Controllers/ElectionController.cs +++ b/electronicvotingfyp/Controllers/ElectionController.cs @@ -77,6 +77,7 @@ namespace electronicvotingfyp.Controllers if (!fc.validFile) { TempData["Message"] = fc.ErrorMessage; + return RedirectToAction("Create", "Election"); } else diff --git a/electronicvotingfyp/Models/DisplayParticipationModel.cs b/electronicvotingfyp/Models/DisplayParticipationModel.cs new file mode 100644 index 0000000000000000000000000000000000000000..248da68055840bbc14ca800faf3d68e375f7c7d4 --- /dev/null +++ b/electronicvotingfyp/Models/DisplayParticipationModel.cs @@ -0,0 +1,10 @@ +using System; +namespace electronicvotingfyp.Models +{ + public class DisplayParticipationModel + { + public List<Election> participations { get; set; } + public List<Election> creations { get; set; } + } +} + diff --git a/electronicvotingfyp/Models/EnableAuthenticator.cs b/electronicvotingfyp/Models/EnableAuthenticator.cs new file mode 100644 index 0000000000000000000000000000000000000000..f5d5e483d2ba4af356953192859b3d9204fbf37f --- /dev/null +++ b/electronicvotingfyp/Models/EnableAuthenticator.cs @@ -0,0 +1,188 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#nullable disable + +using System; +using System.ComponentModel.DataAnnotations; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace electronicvotingfyp.Models +{ + public class EnableAuthenticatorModel : PageModel + { + private readonly UserManager<IdentityUser> _userManager; + private readonly ILogger<EnableAuthenticatorModel> _logger; + private readonly UrlEncoder _urlEncoder; + + private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6"; + + public EnableAuthenticatorModel( + UserManager<IdentityUser> userManager, + ILogger<EnableAuthenticatorModel> logger, + UrlEncoder urlEncoder) + { + _userManager = userManager; + _logger = logger; + _urlEncoder = urlEncoder; + } + + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + public string SharedKey { get; set; } + + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + public string AuthenticatorUri { get; set; } + + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + [TempData] + public string[] RecoveryCodes { get; set; } + + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + [TempData] + public string StatusMessage { get; set; } + + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + [BindProperty] + public InputModel Input { get; set; } + + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + public class InputModel + { + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + [Required] + [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Text)] + [Display(Name = "Verification Code")] + public string Code { get; set; } + } + + public async Task<IActionResult> OnGetAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + await LoadSharedKeyAndQrCodeUriAsync(user); + + return Page(); + } + + public async Task<IActionResult> OnPostAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + if (!ModelState.IsValid) + { + await LoadSharedKeyAndQrCodeUriAsync(user); + return Page(); + } + + // Strip spaces and hyphens + var verificationCode = Input.Code.Replace(" ", string.Empty).Replace("-", string.Empty); + + var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync( + user, _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode); + + if (!is2faTokenValid) + { + ModelState.AddModelError("Input.Code", "Verification code is invalid."); + await LoadSharedKeyAndQrCodeUriAsync(user); + return Page(); + } + + await _userManager.SetTwoFactorEnabledAsync(user, true); + var userId = await _userManager.GetUserIdAsync(user); + _logger.LogInformation("User with ID '{UserId}' has enabled 2FA with an authenticator app.", userId); + + StatusMessage = "Your authenticator app has been verified."; + + if (await _userManager.CountRecoveryCodesAsync(user) == 0) + { + var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); + RecoveryCodes = recoveryCodes.ToArray(); + return RedirectToPage("./ShowRecoveryCodes"); + } + else + { + return RedirectToPage("./TwoFactorAuthentication"); + } + } + + private async Task LoadSharedKeyAndQrCodeUriAsync(IdentityUser user) + { + // Load the authenticator key & QR code URI to display on the form + var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user); + if (string.IsNullOrEmpty(unformattedKey)) + { + await _userManager.ResetAuthenticatorKeyAsync(user); + unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user); + } + + SharedKey = FormatKey(unformattedKey); + + var email = await _userManager.GetEmailAsync(user); + AuthenticatorUri = GenerateQrCodeUri(email, unformattedKey); + } + + private string FormatKey(string unformattedKey) + { + var result = new StringBuilder(); + int currentPosition = 0; + while (currentPosition + 4 < unformattedKey.Length) + { + result.Append(unformattedKey.AsSpan(currentPosition, 4)).Append(' '); + currentPosition += 4; + } + if (currentPosition < unformattedKey.Length) + { + result.Append(unformattedKey.AsSpan(currentPosition)); + } + + return result.ToString().ToLowerInvariant(); + } + + private string GenerateQrCodeUri(string email, string unformattedKey) + { + return string.Format( + CultureInfo.InvariantCulture, + AuthenticatorUriFormat, + _urlEncoder.Encode("Microsoft.AspNetCore.Identity.UI"), + _urlEncoder.Encode(email), + unformattedKey); + } + } +} diff --git a/electronicvotingfyp/Models/ManageNavPages.cs b/electronicvotingfyp/Models/ManageNavPages.cs new file mode 100644 index 0000000000000000000000000000000000000000..58381bb57c058286ec9a8c954af7b36039baa57d --- /dev/null +++ b/electronicvotingfyp/Models/ManageNavPages.cs @@ -0,0 +1,123 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#nullable disable + +using System; +using Microsoft.AspNetCore.Mvc.Rendering; + +namespace electronicvotingfyp.Models +{ + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + public static class ManageNavPages + { + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + public static string Index => "Index"; + + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + public static string Email => "Email"; + + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + public static string ChangePassword => "ChangePassword"; + + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + public static string DownloadPersonalData => "DownloadPersonalData"; + + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + public static string DeletePersonalData => "DeletePersonalData"; + + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + public static string ExternalLogins => "ExternalLogins"; + + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + public static string PersonalData => "PersonalData"; + + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + public static string TwoFactorAuthentication => "TwoFactorAuthentication"; + + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + public static string IndexNavClass(ViewContext viewContext) => PageNavClass(viewContext, Index); + + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + public static string EmailNavClass(ViewContext viewContext) => PageNavClass(viewContext, Email); + + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + public static string ChangePasswordNavClass(ViewContext viewContext) => PageNavClass(viewContext, ChangePassword); + + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + public static string DownloadPersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, DownloadPersonalData); + + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + public static string DeletePersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, DeletePersonalData); + + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + public static string ExternalLoginsNavClass(ViewContext viewContext) => PageNavClass(viewContext, ExternalLogins); + + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + public static string PersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, PersonalData); + + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) => PageNavClass(viewContext, TwoFactorAuthentication); + + /// <summary> + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// </summary> + public static string PageNavClass(ViewContext viewContext, string page) + { + var activePage = viewContext.ViewData["ActivePage"] as string + ?? System.IO.Path.GetFileNameWithoutExtension(viewContext.ActionDescriptor.DisplayName); + return string.Equals(activePage, page, StringComparison.OrdinalIgnoreCase) ? "active" : null; + } + } +} diff --git a/electronicvotingfyp/Views/Account/EnableAuthenticator.cshtml b/electronicvotingfyp/Views/Account/EnableAuthenticator.cshtml index 1be39b62f37d54c58bddf2d344cb30a5b5038101..7319cd74f7665a2f760d2be7baaeab459fcee916 100644 --- a/electronicvotingfyp/Views/Account/EnableAuthenticator.cshtml +++ b/electronicvotingfyp/Views/Account/EnableAuthenticator.cshtml @@ -1,7 +1,7 @@ @page @model EnableAuthenticatorModel @{ - ViewData["Title"] = "Configure authenticator app"; + //ViewData["Title"] = "Configure authenticator app"; ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; } diff --git a/electronicvotingfyp/electronicvotingfyp.csproj b/electronicvotingfyp/electronicvotingfyp.csproj index 603ad01004c9469ab82926824e86fdf9a5a4933e..3ae7efe8ea77acc5b44fc143819f7d4bfffc3435 100644 --- a/electronicvotingfyp/electronicvotingfyp.csproj +++ b/electronicvotingfyp/electronicvotingfyp.csproj @@ -77,7 +77,6 @@ <Content Remove="wwwroot\js\library.js" /> <Content Remove="wwwroot\js\library.js.map" /> <Content Remove="wwwroot\js\app.js" /> - <Content Remove="Views\Account\EnableAuthenticator.cshtml" /> <Content Remove="wwwroot\images\" /> <Content Remove="wwwroot\images\20945538.jpg" /> <Content Remove="wwwroot\images\icons8-add-new-100.png" />